mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Extract ND into own class
This commit is contained in:
@@ -15,7 +15,13 @@ export class AuthFailure extends Error {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type IdName = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type ArtistSummary = {
|
export type ArtistSummary = {
|
||||||
|
// todo: why can this be undefined?
|
||||||
id: string | undefined;
|
id: string | undefined;
|
||||||
name: string;
|
name: string;
|
||||||
image: BUrn | undefined;
|
image: BUrn | undefined;
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import { Md5 } from "ts-md5/dist/md5";
|
|||||||
import {
|
import {
|
||||||
Credentials,
|
Credentials,
|
||||||
MusicService,
|
MusicService,
|
||||||
Result,
|
|
||||||
ArtistQuery,
|
|
||||||
MusicLibrary,
|
MusicLibrary,
|
||||||
Track,
|
Track,
|
||||||
AuthFailure,
|
AuthFailure,
|
||||||
@@ -19,8 +17,7 @@ import randomstring from "randomstring";
|
|||||||
import { b64Encode, b64Decode } from "./b64";
|
import { b64Encode, b64Decode } from "./b64";
|
||||||
import { axiosImageFetcher, ImageFetcher } from "./images";
|
import { axiosImageFetcher, ImageFetcher } from "./images";
|
||||||
import { asURLSearchParams } from "./utils";
|
import { asURLSearchParams } from "./utils";
|
||||||
import { artistImageURN, SubsonicGenericMusicLibrary } from "./subsonic/generic";
|
import { artistImageURN, NaivdromeMusicLibrary, SubsonicGenericMusicLibrary } from "./subsonic/generic";
|
||||||
|
|
||||||
|
|
||||||
export const t = (password: string, s: string) =>
|
export const t = (password: string, s: string) =>
|
||||||
Md5.hashStr(`${password}${s}`);
|
Md5.hashStr(`${password}${s}`);
|
||||||
@@ -96,6 +93,7 @@ export type SubsonicCredentials = Credentials & {
|
|||||||
|
|
||||||
export const asToken = (credentials: SubsonicCredentials) =>
|
export const asToken = (credentials: SubsonicCredentials) =>
|
||||||
b64Encode(JSON.stringify(credentials));
|
b64Encode(JSON.stringify(credentials));
|
||||||
|
|
||||||
export const parseToken = (token: string): SubsonicCredentials =>
|
export const parseToken = (token: string): SubsonicCredentials =>
|
||||||
JSON.parse(b64Decode(token));
|
JSON.parse(b64Decode(token));
|
||||||
|
|
||||||
@@ -216,66 +214,10 @@ export class Subsonic implements MusicService {
|
|||||||
private libraryFor = (
|
private libraryFor = (
|
||||||
credentials: SubsonicCredentials
|
credentials: SubsonicCredentials
|
||||||
): Promise<SubsonicMusicLibrary> => {
|
): Promise<SubsonicMusicLibrary> => {
|
||||||
const genericSubsonic: SubsonicMusicLibrary =
|
|
||||||
new SubsonicGenericMusicLibrary(this, credentials);
|
|
||||||
|
|
||||||
if (credentials.type == "navidrome") {
|
if (credentials.type == "navidrome") {
|
||||||
return Promise.resolve({
|
return Promise.resolve(new NaivdromeMusicLibrary(this, credentials));
|
||||||
...genericSubsonic,
|
|
||||||
flavour: () => "navidrome",
|
|
||||||
bearerToken: (credentials: Credentials) =>
|
|
||||||
pipe(
|
|
||||||
TE.tryCatch(
|
|
||||||
() =>
|
|
||||||
axios.post(
|
|
||||||
`${this.url}/auth/login`,
|
|
||||||
_.pick(credentials, "username", "password")
|
|
||||||
),
|
|
||||||
() => new AuthFailure("Failed to get bearerToken")
|
|
||||||
),
|
|
||||||
TE.map((it) => it.data.token as string | undefined)
|
|
||||||
),
|
|
||||||
artists: async (
|
|
||||||
q: ArtistQuery
|
|
||||||
): Promise<Result<ArtistSummary & Sortable>> => {
|
|
||||||
let params: any = {
|
|
||||||
_sort: "name",
|
|
||||||
_order: "ASC",
|
|
||||||
_start: q._index || "0",
|
|
||||||
};
|
|
||||||
if (q._count) {
|
|
||||||
params = {
|
|
||||||
...params,
|
|
||||||
_end: (q._index || 0) + q._count,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return axios
|
|
||||||
.get(`${this.url}/api/artist`, {
|
|
||||||
params: asURLSearchParams(params),
|
|
||||||
headers: {
|
|
||||||
"User-Agent": USER_AGENT,
|
|
||||||
"x-nd-authorization": `Bearer ${credentials.bearer}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
throw `Navidrome failed with: ${e}`;
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.status != 200 && response.status != 206) {
|
|
||||||
throw `Navidrome failed with a ${
|
|
||||||
response.status || "no!"
|
|
||||||
} status`;
|
|
||||||
} else return response;
|
|
||||||
})
|
|
||||||
.then((it) => ({
|
|
||||||
results: (it.data as NDArtist[]).map(artistSummaryFromNDArtist),
|
|
||||||
total: Number.parseInt(it.headers["x-total-count"] || "0"),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve(genericSubsonic);
|
return Promise.resolve(new SubsonicGenericMusicLibrary(this, credentials));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ import * as A from "fp-ts/Array";
|
|||||||
import { pipe } from "fp-ts/lib/function";
|
import { pipe } from "fp-ts/lib/function";
|
||||||
import { ordString } from "fp-ts/lib/Ord";
|
import { ordString } from "fp-ts/lib/Ord";
|
||||||
import { inject } from 'underscore';
|
import { inject } from 'underscore';
|
||||||
|
import _ from "underscore";
|
||||||
|
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { b64Decode, b64Encode } from "../b64";
|
import { b64Decode, b64Encode } from "../b64";
|
||||||
import { assertSystem, BUrn } from "../burn";
|
import { assertSystem, BUrn } from "../burn";
|
||||||
|
|
||||||
import { Album, AlbumQuery, AlbumQueryType, AlbumSummary, Artist, ArtistQuery, Credentials, Genre, Rating, Result, slice2, Sortable, Track } from "../music_service";
|
import { Album, AlbumQuery, AlbumQueryType, AlbumSummary, Artist, ArtistQuery, ArtistSummary, AuthFailure, Credentials, Genre, IdName, Rating, Result, slice2, Sortable, Track } from "../music_service";
|
||||||
import Subsonic, { DODGY_IMAGE_NAME, SubsonicCredentials, SubsonicMusicLibrary, SubsonicResponse, USER_AGENT } from "../subsonic";
|
import Subsonic, { artistSummaryFromNDArtist, DODGY_IMAGE_NAME, NDArtist, SubsonicCredentials, SubsonicMusicLibrary, SubsonicResponse, USER_AGENT } from "../subsonic";
|
||||||
|
import axios from "axios";
|
||||||
|
import { asURLSearchParams } from "../utils";
|
||||||
|
|
||||||
|
|
||||||
type album = {
|
type album = {
|
||||||
@@ -81,14 +84,6 @@ type artistInfo = images & {
|
|||||||
similarArtist: artist[];
|
similarArtist: artist[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type IdName = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ArtistSummary = IdName & {
|
|
||||||
image: BUrn | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type song = {
|
export type song = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -272,9 +267,11 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
|
|
||||||
flavour = () => "subsonic";
|
flavour = () => "subsonic";
|
||||||
|
|
||||||
bearerToken = (_: Credentials) => TE.right(undefined);
|
bearerToken = (_: Credentials): TE.TaskEither<Error, string | undefined> => TE.right(undefined);
|
||||||
|
|
||||||
artists = (q: ArtistQuery): Promise<Result<ArtistSummary & Sortable>> =>
|
artists = async (
|
||||||
|
q: ArtistQuery
|
||||||
|
): Promise<Result<ArtistSummary & Sortable>> =>
|
||||||
this.getArtists()
|
this.getArtists()
|
||||||
.then(slice2(q))
|
.then(slice2(q))
|
||||||
.then(([page, total]) => ({
|
.then(([page, total]) => ({
|
||||||
@@ -725,4 +722,67 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
results: albums.slice(0, q._count),
|
results: albums.slice(0, q._count),
|
||||||
total: albums.length == 500 ? total : (q._index || 0) + albums.length,
|
total: albums.length == 500 ? total : (q._index || 0) + albums.length,
|
||||||
}));
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export class NaivdromeMusicLibrary extends SubsonicGenericMusicLibrary {
|
||||||
|
|
||||||
|
constructor(subsonic: Subsonic, credentials: SubsonicCredentials) {
|
||||||
|
super(subsonic, credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
flavour = () => "navidrome";
|
||||||
|
|
||||||
|
bearerToken = (credentials: Credentials): TE.TaskEither<Error, string | undefined> =>
|
||||||
|
pipe(
|
||||||
|
TE.tryCatch(
|
||||||
|
() =>
|
||||||
|
axios.post(
|
||||||
|
`${this.subsonic.url}/auth/login`,
|
||||||
|
_.pick(credentials, "username", "password")
|
||||||
|
),
|
||||||
|
() => new AuthFailure("Failed to get bearerToken")
|
||||||
|
),
|
||||||
|
TE.map((it) => it.data.token as string | undefined)
|
||||||
|
);
|
||||||
|
|
||||||
|
artists = async (
|
||||||
|
q: ArtistQuery
|
||||||
|
): Promise<Result<ArtistSummary & Sortable>> => {
|
||||||
|
let params: any = {
|
||||||
|
_sort: "name",
|
||||||
|
_order: "ASC",
|
||||||
|
_start: q._index || "0",
|
||||||
|
};
|
||||||
|
if (q._count) {
|
||||||
|
params = {
|
||||||
|
...params,
|
||||||
|
_end: (q._index || 0) + q._count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const x: Promise<Result<ArtistSummary & Sortable>> = axios
|
||||||
|
.get(`${this.subsonic.url}/api/artist`, {
|
||||||
|
params: asURLSearchParams(params),
|
||||||
|
headers: {
|
||||||
|
"User-Agent": USER_AGENT,
|
||||||
|
"x-nd-authorization": `Bearer ${this.credentials.bearer}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
throw `Navidrome failed with: ${e}`;
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status != 200 && response.status != 206) {
|
||||||
|
throw `Navidrome failed with a ${
|
||||||
|
response.status || "no!"
|
||||||
|
} status`;
|
||||||
|
} else return response;
|
||||||
|
})
|
||||||
|
.then((it) => ({
|
||||||
|
results: (it.data as NDArtist[]).map(artistSummaryFromNDArtist),
|
||||||
|
total: Number.parseInt(it.headers["x-total-count"] || "0"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user