mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
tests passing
This commit is contained in:
@@ -23,7 +23,8 @@ export type ArtistSummary = {
|
||||
|
||||
export type SimilarArtist = ArtistSummary & { inLibrary: boolean };
|
||||
|
||||
export type Artist = ArtistSummary & {
|
||||
// todo: maybe is should be artist.summary rather than an artist also being a summary?
|
||||
export type Artist = Pick<ArtistSummary, "id" | "name" | "image"> & {
|
||||
albums: AlbumSummary[];
|
||||
similarArtists: SimilarArtist[]
|
||||
};
|
||||
@@ -34,12 +35,11 @@ export type AlbumSummary = {
|
||||
year: string | undefined;
|
||||
genre: Genre | undefined;
|
||||
coverArt: BUrn | undefined;
|
||||
|
||||
artistName: string | undefined;
|
||||
artistId: string | undefined;
|
||||
};
|
||||
|
||||
export type Album = AlbumSummary & {};
|
||||
export type Album = Pick<AlbumSummary, "id" | "name" | "year" | "genre" | "coverArt" | "artistName" | "artistId"> & { tracks: Track[] };
|
||||
|
||||
export type Genre = {
|
||||
name: string;
|
||||
@@ -175,7 +175,7 @@ export interface MusicLibrary {
|
||||
artists(q: ArtistQuery): Promise<Result<ArtistSummary>>;
|
||||
artist(id: string): Promise<Artist>;
|
||||
albums(q: AlbumQuery): Promise<Result<AlbumSummary>>;
|
||||
album(id: string): Promise<Album>;
|
||||
album(id: string): Promise<AlbumSummary>;
|
||||
tracks(albumId: string): Promise<Track[]>;
|
||||
track(trackId: string): Promise<Track>;
|
||||
genres(): Promise<Genre[]>;
|
||||
|
||||
@@ -10,7 +10,6 @@ import logger from "./logger";
|
||||
|
||||
import { LinkCodes } from "./link_codes";
|
||||
import {
|
||||
Album,
|
||||
AlbumQuery,
|
||||
AlbumSummary,
|
||||
ArtistSummary,
|
||||
@@ -612,7 +611,7 @@ function bindSmapiSoapServiceToExpress(
|
||||
switch (type) {
|
||||
case "artist":
|
||||
return musicLibrary.artist(typeId).then((artist) => {
|
||||
const [page, total] = slice2<Album>(paging)(
|
||||
const [page, total] = slice2<AlbumSummary>(paging)(
|
||||
artist.albums
|
||||
);
|
||||
return {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
AlbumQueryType,
|
||||
PlaylistSummary,
|
||||
Encoding,
|
||||
albumToAlbumSummary,
|
||||
} from "./music_library";
|
||||
import sharp from "sharp";
|
||||
import _ from "underscore";
|
||||
@@ -24,7 +25,7 @@ import axios, { AxiosRequestConfig } from "axios";
|
||||
import randomstring from "randomstring";
|
||||
import { b64Encode, b64Decode } from "./b64";
|
||||
import { BUrn } from "./burn";
|
||||
import { artist } from "./smapi";
|
||||
import { album, artist } from "./smapi";
|
||||
import { URLBuilder } from "./url_builder";
|
||||
|
||||
export const BROWSER_HEADERS = {
|
||||
@@ -159,6 +160,7 @@ export type song = {
|
||||
transcodedContentType: string | undefined;
|
||||
type: string | undefined;
|
||||
userRating: number | undefined;
|
||||
// todo: this field shouldnt be on song?
|
||||
starred: string | undefined;
|
||||
};
|
||||
|
||||
@@ -279,7 +281,7 @@ export const artistImageURN = (
|
||||
};
|
||||
|
||||
export const asTrack = (
|
||||
album: Album,
|
||||
album: AlbumSummary,
|
||||
song: song,
|
||||
customPlayers: CustomPlayers
|
||||
): Track => ({
|
||||
@@ -298,7 +300,7 @@ export const asTrack = (
|
||||
number: song.track || 0,
|
||||
genre: maybeAsGenre(song.genre),
|
||||
coverArt: coverArtURN(song.coverArt),
|
||||
album,
|
||||
album: album,
|
||||
artist: {
|
||||
id: song.artistId,
|
||||
name: song.artist ? song.artist : "?",
|
||||
@@ -315,7 +317,7 @@ export const asTrack = (
|
||||
},
|
||||
});
|
||||
|
||||
export const asAlbum = (album: album): Album => ({
|
||||
export const asAlbumSummary = (album: album): AlbumSummary => ({
|
||||
id: album.id,
|
||||
name: album.name,
|
||||
year: album.year,
|
||||
@@ -412,7 +414,7 @@ export const asURLSearchParams = (q: any) => {
|
||||
export type ImageFetcher = (url: string) => Promise<CoverArt | undefined>;
|
||||
|
||||
export const cachingImageFetcher =
|
||||
(cacheDir: string, delegate: ImageFetcher) =>
|
||||
(cacheDir: string, delegate: ImageFetcher, makeSharp = sharp) =>
|
||||
async (url: string): Promise<CoverArt | undefined> => {
|
||||
const filename = path.join(cacheDir, `${Md5.hashStr(url)}.png`);
|
||||
return fse
|
||||
@@ -421,7 +423,7 @@ export const cachingImageFetcher =
|
||||
.catch(() =>
|
||||
delegate(url).then((image) => {
|
||||
if (image) {
|
||||
return sharp(image.data)
|
||||
return makeSharp(image.data)
|
||||
.png()
|
||||
.toBuffer()
|
||||
.then((png) => {
|
||||
@@ -584,19 +586,30 @@ export class Subsonic {
|
||||
})),
|
||||
}));
|
||||
|
||||
getAlbum = (credentials: Credentials, id: string): Promise<Album> =>
|
||||
getAlbum = (credentials: Credentials, id: string): Promise<Album> =>
|
||||
this.getJSON<GetAlbumResponse>(credentials, "/rest/getAlbum", { id })
|
||||
.then((it) => it.album)
|
||||
.then((album) => ({
|
||||
id: album.id,
|
||||
name: album.name,
|
||||
year: album.year,
|
||||
genre: maybeAsGenre(album.genre),
|
||||
artistId: album.artistId,
|
||||
artistName: album.artist,
|
||||
coverArt: coverArtURN(album.coverArt),
|
||||
}));
|
||||
|
||||
.then((album) => {
|
||||
const x: AlbumSummary = {
|
||||
id: album.id,
|
||||
name: album.name,
|
||||
year: album.year,
|
||||
genre: maybeAsGenre(album.genre),
|
||||
artistId: album.artistId,
|
||||
artistName: album.artist,
|
||||
coverArt: coverArtURN(album.coverArt)
|
||||
}
|
||||
return { summary: x, songs: album.song }
|
||||
}).then(({ summary, songs }) => {
|
||||
const x: AlbumSummary = summary
|
||||
const y: Track[] = songs.map((it) => asTrack(summary, it, this.customPlayers))
|
||||
return {
|
||||
...x,
|
||||
tracks: y
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
getArtist = (
|
||||
credentials: Credentials,
|
||||
id: string
|
||||
@@ -647,7 +660,7 @@ export class Subsonic {
|
||||
.then((it) => it.song)
|
||||
.then((song) =>
|
||||
this.getAlbum(credentials, song.albumId!).then((album) =>
|
||||
asTrack(album, song, this.customPlayers)
|
||||
asTrack(albumToAlbumSummary(album), song, this.customPlayers)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Credentials,
|
||||
MusicService,
|
||||
ArtistSummary,
|
||||
Album,
|
||||
Result,
|
||||
slice2,
|
||||
AlbumQuery,
|
||||
@@ -15,13 +14,14 @@ import {
|
||||
Artist,
|
||||
AuthFailure,
|
||||
AuthSuccess,
|
||||
albumToAlbumSummary,
|
||||
} from "./music_library";
|
||||
import {
|
||||
Subsonic,
|
||||
CustomPlayers,
|
||||
GetAlbumResponse,
|
||||
asTrack,
|
||||
asAlbum,
|
||||
asAlbumSummary,
|
||||
PingResponse,
|
||||
NO_CUSTOM_PLAYERS,
|
||||
asToken,
|
||||
@@ -171,11 +171,13 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
||||
albums = async (q: AlbumQuery): Promise<Result<AlbumSummary>> =>
|
||||
this.subsonic.getAlbumList2(this.credentials, q);
|
||||
|
||||
album = (id: string): Promise<Album> =>
|
||||
this.subsonic.getAlbum(this.credentials, id);
|
||||
// todo: this should probably return an Album
|
||||
album = (id: string): Promise<AlbumSummary> =>
|
||||
this.subsonic.getAlbum(this.credentials, id).then(albumToAlbumSummary);
|
||||
|
||||
genres = () => this.subsonic.getGenres(this.credentials);
|
||||
|
||||
// todo: do we even need this if Album has tracks?
|
||||
tracks = (albumId: string) =>
|
||||
this.subsonic
|
||||
.getJSON<GetAlbumResponse>(this.credentials, "/rest/getAlbum", {
|
||||
@@ -184,7 +186,7 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
||||
.then((it) => it.album)
|
||||
.then((album) =>
|
||||
(album.song || []).map((song) =>
|
||||
asTrack(asAlbum(album), song, this.customPlayers)
|
||||
asTrack(asAlbumSummary(album), song, this.customPlayers)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -418,7 +420,7 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
||||
songs.map((song) =>
|
||||
this.subsonic
|
||||
.getAlbum(this.credentials, song.albumId!)
|
||||
.then((album) => asTrack(album, song, this.customPlayers))
|
||||
.then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers))
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -436,7 +438,7 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
||||
songs.map((song) =>
|
||||
this.subsonic
|
||||
.getAlbum(this.credentials, song.albumId!)
|
||||
.then((album) => asTrack(album, song, this.customPlayers))
|
||||
.then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user