mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Genre with id and name, rather than just name
This commit is contained in:
@@ -49,18 +49,23 @@ export type AlbumSummary = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
year: string | undefined;
|
year: string | undefined;
|
||||||
genre: string | undefined;
|
genre: Genre | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Album = AlbumSummary & {};
|
export type Album = AlbumSummary & {};
|
||||||
|
|
||||||
|
export type Genre = {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type Track = {
|
export type Track = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
mimeType: string;
|
mimeType: string;
|
||||||
duration: number;
|
duration: number;
|
||||||
number: number | undefined;
|
number: number | undefined;
|
||||||
genre: string | undefined;
|
genre: Genre | undefined;
|
||||||
album: AlbumSummary;
|
album: AlbumSummary;
|
||||||
artist: ArtistSummary;
|
artist: ArtistSummary;
|
||||||
};
|
};
|
||||||
@@ -137,7 +142,7 @@ export interface MusicLibrary {
|
|||||||
album(id: string): Promise<Album>;
|
album(id: string): Promise<Album>;
|
||||||
tracks(albumId: string): Promise<Track[]>;
|
tracks(albumId: string): Promise<Track[]>;
|
||||||
track(trackId: string): Promise<Track>;
|
track(trackId: string): Promise<Track>;
|
||||||
genres(): Promise<string[]>;
|
genres(): Promise<Genre[]>;
|
||||||
stream({
|
stream({
|
||||||
trackId,
|
trackId,
|
||||||
range,
|
range,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
Images,
|
Images,
|
||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
NO_IMAGES,
|
NO_IMAGES,
|
||||||
|
Genre,
|
||||||
} from "./music_service";
|
} from "./music_service";
|
||||||
import X2JS from "x2js";
|
import X2JS from "x2js";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
@@ -193,7 +194,7 @@ const asTrack = (album: Album, song: song) => ({
|
|||||||
mimeType: song._contentType,
|
mimeType: song._contentType,
|
||||||
duration: parseInt(song._duration || "0"),
|
duration: parseInt(song._duration || "0"),
|
||||||
number: parseInt(song._track || "0"),
|
number: parseInt(song._track || "0"),
|
||||||
genre: song._genre,
|
genre: maybeAsGenre(song._genre),
|
||||||
album,
|
album,
|
||||||
artist: {
|
artist: {
|
||||||
id: song._artistId,
|
id: song._artistId,
|
||||||
@@ -206,9 +207,18 @@ const asAlbum = (album: album) => ({
|
|||||||
id: album._id,
|
id: album._id,
|
||||||
name: album._name,
|
name: album._name,
|
||||||
year: album._year,
|
year: album._year,
|
||||||
genre: album._genre,
|
genre: maybeAsGenre(album._genre),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const asGenre = (genreName: string) => ({ id: genreName, name: genreName });
|
||||||
|
|
||||||
|
const maybeAsGenre = (genreName: string | undefined): Genre | undefined =>
|
||||||
|
pipe(
|
||||||
|
O.fromNullable(genreName),
|
||||||
|
O.map(asGenre),
|
||||||
|
O.getOrElseW(() => undefined)
|
||||||
|
);
|
||||||
|
|
||||||
export class Navidrome implements MusicService {
|
export class Navidrome implements MusicService {
|
||||||
url: string;
|
url: string;
|
||||||
encryption: Encryption;
|
encryption: Encryption;
|
||||||
@@ -318,7 +328,7 @@ export class Navidrome implements MusicService {
|
|||||||
id: album._id,
|
id: album._id,
|
||||||
name: album._name,
|
name: album._name,
|
||||||
year: album._year,
|
year: album._year,
|
||||||
genre: album._genre,
|
genre: maybeAsGenre(album._genre),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
getArtist = (
|
getArtist = (
|
||||||
@@ -336,7 +346,7 @@ export class Navidrome implements MusicService {
|
|||||||
id: album._id,
|
id: album._id,
|
||||||
name: album._name,
|
name: album._name,
|
||||||
year: album._year,
|
year: album._year,
|
||||||
genre: album._genre,
|
genre: maybeAsGenre(album._genre),
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -399,7 +409,7 @@ export class Navidrome implements MusicService {
|
|||||||
id: album._id,
|
id: album._id,
|
||||||
name: album._name,
|
name: album._name,
|
||||||
year: album._year,
|
year: album._year,
|
||||||
genre: album._genre,
|
genre: maybeAsGenre(album._genre),
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
.then(slice2(q))
|
.then(slice2(q))
|
||||||
@@ -416,7 +426,8 @@ export class Navidrome implements MusicService {
|
|||||||
pipe(
|
pipe(
|
||||||
it.genres.genre,
|
it.genres.genre,
|
||||||
A.map((it) => it.__text),
|
A.map((it) => it.__text),
|
||||||
A.sort(ordString)
|
A.sort(ordString),
|
||||||
|
A.map((it) => ({ id: it, name: it }))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
tracks: (albumId: string) =>
|
tracks: (albumId: string) =>
|
||||||
|
|||||||
11
src/smapi.ts
11
src/smapi.ts
@@ -10,6 +10,7 @@ import {
|
|||||||
Album,
|
Album,
|
||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
ArtistSummary,
|
ArtistSummary,
|
||||||
|
Genre,
|
||||||
MusicLibrary,
|
MusicLibrary,
|
||||||
MusicService,
|
MusicService,
|
||||||
slice2,
|
slice2,
|
||||||
@@ -184,10 +185,10 @@ const container = ({
|
|||||||
title,
|
title,
|
||||||
});
|
});
|
||||||
|
|
||||||
const genre = (genre: string) => ({
|
const genre = (genre: Genre) => ({
|
||||||
itemType: "container",
|
itemType: "container",
|
||||||
id: `genre:${genre}`,
|
id: `genre:${genre.id}`,
|
||||||
title: genre,
|
title: genre.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const defaultAlbumArtURI = (
|
export const defaultAlbumArtURI = (
|
||||||
@@ -235,8 +236,8 @@ export const track = (
|
|||||||
artist: track.artist.name,
|
artist: track.artist.name,
|
||||||
artistId: track.artist.id,
|
artistId: track.artist.id,
|
||||||
duration: track.duration,
|
duration: track.duration,
|
||||||
genre: track.album.genre,
|
genre: track.album.genre?.name,
|
||||||
// genreId
|
genreId: track.album.genre?.id,
|
||||||
trackNumber: track.number,
|
trackNumber: track.number,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -80,15 +80,26 @@ export function anArtist(fields: Partial<Artist> = {}): Artist {
|
|||||||
large: `/artist/art/${id}/large`,
|
large: `/artist/art/${id}/large`,
|
||||||
},
|
},
|
||||||
similarArtists: [
|
similarArtists: [
|
||||||
{ id: uuid(), name: "Similar artist1"},
|
{ id: uuid(), name: "Similar artist1" },
|
||||||
{ id: uuid(), name: "Similar artist2"},
|
{ id: uuid(), name: "Similar artist2" },
|
||||||
],
|
],
|
||||||
...fields,
|
...fields,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SAMPLE_GENRES = ["Metal", "Pop", "Rock", "Hip-Hop"]
|
export const HIP_HOP = { id: "genre_hip_hop", name: "Hip-Hop" };
|
||||||
export const randomGenre = () => SAMPLE_GENRES[randomInt(SAMPLE_GENRES.length)]
|
export const METAL = { id: "genre_metal", name: "Metal" };
|
||||||
|
export const NEW_WAVE = { id: "genre_new_wave", name: "New Wave" };
|
||||||
|
export const POP = { id: "genre_pop", name: "Pop" };
|
||||||
|
export const POP_ROCK = { id: "genre_pop_rock", name: "Pop Rock" };
|
||||||
|
export const REGGAE = { id: "genre_reggae", name: "Reggae" };
|
||||||
|
export const ROCK = { id: "genre_rock", name: "Rock" };
|
||||||
|
export const SKA = { id: "genre_ska", name: "Ska" };
|
||||||
|
export const PUNK = { id: "genre_punk", name: "Punk" };
|
||||||
|
export const TRIP_HOP = { id: "genre_trip_hop", name: "Trip Hop" };
|
||||||
|
|
||||||
|
export const SAMPLE_GENRES = [HIP_HOP, METAL, NEW_WAVE, POP, POP_ROCK, REGGAE, ROCK, SKA];
|
||||||
|
export const randomGenre = () => SAMPLE_GENRES[randomInt(SAMPLE_GENRES.length)];
|
||||||
|
|
||||||
export function aTrack(fields: Partial<Track> = {}): Track {
|
export function aTrack(fields: Partial<Track> = {}): Track {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
@@ -101,8 +112,8 @@ export function aTrack(fields: Partial<Track> = {}): Track {
|
|||||||
genre: randomGenre(),
|
genre: randomGenre(),
|
||||||
artist: anArtist(),
|
artist: anArtist(),
|
||||||
album: anAlbum(),
|
album: anAlbum(),
|
||||||
...fields
|
...fields,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function anAlbum(fields: Partial<Album> = {}): Album {
|
export function anAlbum(fields: Partial<Album> = {}): Album {
|
||||||
@@ -124,13 +135,13 @@ export const BLONDIE: Artist = {
|
|||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: "Blondie",
|
name: "Blondie",
|
||||||
year: "1976",
|
year: "1976",
|
||||||
genre: "New Wave",
|
genre: NEW_WAVE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: "Parallel Lines",
|
name: "Parallel Lines",
|
||||||
year: "1978",
|
year: "1978",
|
||||||
genre: "Pop Rock",
|
genre: POP_ROCK,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
image: {
|
image: {
|
||||||
@@ -138,23 +149,23 @@ export const BLONDIE: Artist = {
|
|||||||
medium: undefined,
|
medium: undefined,
|
||||||
large: undefined,
|
large: undefined,
|
||||||
},
|
},
|
||||||
similarArtists: []
|
similarArtists: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BOB_MARLEY: Artist = {
|
export const BOB_MARLEY: Artist = {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: "Bob Marley",
|
name: "Bob Marley",
|
||||||
albums: [
|
albums: [
|
||||||
{ id: uuid(), name: "Burin'", year: "1973", genre: "Reggae", },
|
{ id: uuid(), name: "Burin'", year: "1973", genre: REGGAE },
|
||||||
{ id: uuid(), name: "Exodus", year: "1977", genre: "Reggae", },
|
{ id: uuid(), name: "Exodus", year: "1977", genre: REGGAE },
|
||||||
{ id: uuid(), name: "Kaya", year: "1978", genre: "Ska", },
|
{ id: uuid(), name: "Kaya", year: "1978", genre: SKA },
|
||||||
],
|
],
|
||||||
image: {
|
image: {
|
||||||
small: "http://localhost/BOB_MARLEY/sml",
|
small: "http://localhost/BOB_MARLEY/sml",
|
||||||
medium: "http://localhost/BOB_MARLEY/med",
|
medium: "http://localhost/BOB_MARLEY/med",
|
||||||
large: "http://localhost/BOB_MARLEY/lge",
|
large: "http://localhost/BOB_MARLEY/lge",
|
||||||
},
|
},
|
||||||
similarArtists: []
|
similarArtists: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MADONNA: Artist = {
|
export const MADONNA: Artist = {
|
||||||
@@ -166,7 +177,7 @@ export const MADONNA: Artist = {
|
|||||||
medium: undefined,
|
medium: undefined,
|
||||||
large: "http://localhost/MADONNA/lge",
|
large: "http://localhost/MADONNA/lge",
|
||||||
},
|
},
|
||||||
similarArtists: []
|
similarArtists: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const METALLICA: Artist = {
|
export const METALLICA: Artist = {
|
||||||
@@ -177,13 +188,13 @@ export const METALLICA: Artist = {
|
|||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: "Ride the Lightening",
|
name: "Ride the Lightening",
|
||||||
year: "1984",
|
year: "1984",
|
||||||
genre: "Heavy Metal",
|
genre: METAL,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: "Master of Puppets",
|
name: "Master of Puppets",
|
||||||
year: "1986",
|
year: "1986",
|
||||||
genre: "Heavy Metal",
|
genre: METAL,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
image: {
|
image: {
|
||||||
@@ -191,7 +202,7 @@ export const METALLICA: Artist = {
|
|||||||
medium: "http://localhost/METALLICA/med",
|
medium: "http://localhost/METALLICA/med",
|
||||||
large: "http://localhost/METALLICA/lge",
|
large: "http://localhost/METALLICA/lge",
|
||||||
},
|
},
|
||||||
similarArtists: []
|
similarArtists: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ALL_ARTISTS = [BOB_MARLEY, BLONDIE, MADONNA, METALLICA];
|
export const ALL_ARTISTS = [BOB_MARLEY, BLONDIE, MADONNA, METALLICA];
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
albumToAlbumSummary,
|
albumToAlbumSummary,
|
||||||
} from "../src/music_service";
|
} from "../src/music_service";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import { anArtist, anAlbum, aTrack } from "./builders";
|
import { anArtist, anAlbum, aTrack, POP, ROCK, METAL, HIP_HOP, SKA } from "./builders";
|
||||||
|
|
||||||
describe("InMemoryMusicService", () => {
|
describe("InMemoryMusicService", () => {
|
||||||
const service = new InMemoryMusicService();
|
const service = new InMemoryMusicService();
|
||||||
@@ -190,16 +190,16 @@ describe("InMemoryMusicService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("albums", () => {
|
describe("albums", () => {
|
||||||
const artist1_album1 = anAlbum({ genre: "Pop" });
|
const artist1_album1 = anAlbum({ genre: POP });
|
||||||
const artist1_album2 = anAlbum({ genre: "Rock" });
|
const artist1_album2 = anAlbum({ genre: ROCK });
|
||||||
const artist1_album3 = anAlbum({ genre: "Metal" });
|
const artist1_album3 = anAlbum({ genre: METAL });
|
||||||
const artist1_album4 = anAlbum({ genre: "Pop" });
|
const artist1_album4 = anAlbum({ genre: POP });
|
||||||
const artist1_album5 = anAlbum({ genre: "Pop" });
|
const artist1_album5 = anAlbum({ genre: POP });
|
||||||
|
|
||||||
const artist2_album1 = anAlbum({ genre: "Metal" });
|
const artist2_album1 = anAlbum({ genre: METAL });
|
||||||
|
|
||||||
const artist3_album1 = anAlbum({ genre: "Hip-Hop" });
|
const artist3_album1 = anAlbum({ genre: HIP_HOP });
|
||||||
const artist3_album2 = anAlbum({ genre: "Pop" });
|
const artist3_album2 = anAlbum({ genre: POP });
|
||||||
|
|
||||||
const totalAlbumCount = 8;
|
const totalAlbumCount = 8;
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return all the albums of that genre for all the artists", async () => {
|
it("should return all the albums of that genre for all the artists", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
genre: "Pop",
|
genre: POP.id,
|
||||||
_index: 0,
|
_index: 0,
|
||||||
_count: 100,
|
_count: 100,
|
||||||
})
|
})
|
||||||
@@ -300,7 +300,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return only the albums for that page", async () => {
|
it("should return only the albums for that page", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
genre: "Pop",
|
genre: POP.id,
|
||||||
_index: 1,
|
_index: 1,
|
||||||
_count: 2,
|
_count: 2,
|
||||||
})
|
})
|
||||||
@@ -318,7 +318,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return only the albums for the last page", async () => {
|
it("should return only the albums for the last page", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
genre: "Pop",
|
genre: POP.id,
|
||||||
_index: 3,
|
_index: 3,
|
||||||
_count: 100,
|
_count: 100,
|
||||||
})
|
})
|
||||||
@@ -367,16 +367,16 @@ describe("InMemoryMusicService", () => {
|
|||||||
describe("genres", () => {
|
describe("genres", () => {
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
albums: [
|
albums: [
|
||||||
anAlbum({ genre: "Pop" }),
|
anAlbum({ genre: POP }),
|
||||||
anAlbum({ genre: "Rock" }),
|
anAlbum({ genre: ROCK }),
|
||||||
anAlbum({ genre: "Pop" }),
|
anAlbum({ genre: POP }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const artist2 = anArtist({
|
const artist2 = anArtist({
|
||||||
albums: [
|
albums: [
|
||||||
anAlbum({ genre: "Hip-Hop" }),
|
anAlbum({ genre: HIP_HOP }),
|
||||||
anAlbum({ genre: "Rap" }),
|
anAlbum({ genre: SKA }),
|
||||||
anAlbum({ genre: "Pop" }),
|
anAlbum({ genre: POP }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -387,10 +387,10 @@ describe("InMemoryMusicService", () => {
|
|||||||
describe("fetching all in one page", () => {
|
describe("fetching all in one page", () => {
|
||||||
it("should provide an array of artists", async () => {
|
it("should provide an array of artists", async () => {
|
||||||
expect(await musicLibrary.genres()).toEqual([
|
expect(await musicLibrary.genres()).toEqual([
|
||||||
"Hip-Hop",
|
HIP_HOP,
|
||||||
"Pop",
|
POP,
|
||||||
"Rap",
|
ROCK,
|
||||||
"Rock",
|
SKA,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { option as O } from "fp-ts";
|
import { option as O } from "fp-ts";
|
||||||
import * as A from "fp-ts/Array";
|
import * as A from "fp-ts/Array";
|
||||||
import { eqString } from "fp-ts/lib/Eq";
|
import { fromEquals } from "fp-ts/lib/Eq";
|
||||||
import { pipe } from "fp-ts/lib/function";
|
import { pipe } from "fp-ts/lib/function";
|
||||||
import { ordString } from "fp-ts/lib/Ord";
|
import { ordString, fromCompare } from "fp-ts/lib/Ord";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MusicService,
|
MusicService,
|
||||||
@@ -19,13 +19,14 @@ import {
|
|||||||
albumToAlbumSummary,
|
albumToAlbumSummary,
|
||||||
Album,
|
Album,
|
||||||
Track,
|
Track,
|
||||||
|
Genre,
|
||||||
} from "../src/music_service";
|
} from "../src/music_service";
|
||||||
|
|
||||||
type P<T> = (t: T) => boolean;
|
type P<T> = (t: T) => boolean;
|
||||||
const all: P<any> = (_: any) => true;
|
const all: P<any> = (_: any) => true;
|
||||||
|
|
||||||
const albumWithGenre = (genre: string): P<[Artist, Album]> => ([_, album]) =>
|
const albumWithGenre = (genreId: string): P<[Artist, Album]> => ([_, album]) =>
|
||||||
album.genre === genre;
|
album.genre?.id === genreId;
|
||||||
|
|
||||||
export class InMemoryMusicService implements MusicService {
|
export class InMemoryMusicService implements MusicService {
|
||||||
users: Record<string, string> = {};
|
users: Record<string, string> = {};
|
||||||
@@ -103,8 +104,10 @@ export class InMemoryMusicService implements MusicService {
|
|||||||
A.flatten,
|
A.flatten,
|
||||||
A.map((it) => O.fromNullable(it.genre)),
|
A.map((it) => O.fromNullable(it.genre)),
|
||||||
A.compact,
|
A.compact,
|
||||||
A.uniq(eqString),
|
A.uniq(fromEquals((x, y) => x.id === y.id)),
|
||||||
A.sort(ordString)
|
A.sort(
|
||||||
|
fromCompare<Genre>((x, y) => ordString.compare(x.id, y.id))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
tracks: (albumId: string) =>
|
tracks: (albumId: string) =>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
t,
|
t,
|
||||||
BROWSER_HEADERS,
|
BROWSER_HEADERS,
|
||||||
DODGY_IMAGE_NAME,
|
DODGY_IMAGE_NAME,
|
||||||
|
asGenre
|
||||||
} from "../src/navidrome";
|
} from "../src/navidrome";
|
||||||
import encryption from "../src/encryption";
|
import encryption from "../src/encryption";
|
||||||
|
|
||||||
@@ -93,7 +94,7 @@ const albumXml = (
|
|||||||
isDir="true"
|
isDir="true"
|
||||||
title="${album.name}" name="${album.name}" album="${album.name}"
|
title="${album.name}" name="${album.name}" album="${album.name}"
|
||||||
artist="${artist.name}"
|
artist="${artist.name}"
|
||||||
genre="${album.genre}"
|
genre="${album.genre?.name}"
|
||||||
coverArt="foo"
|
coverArt="foo"
|
||||||
duration="123"
|
duration="123"
|
||||||
playCount="4"
|
playCount="4"
|
||||||
@@ -110,7 +111,7 @@ const songXml = (track: Track) => `<song
|
|||||||
album="${track.album.name}"
|
album="${track.album.name}"
|
||||||
artist="${track.artist.name}"
|
artist="${track.artist.name}"
|
||||||
track="${track.number}"
|
track="${track.number}"
|
||||||
genre="${track.genre}"
|
genre="${track.genre?.name}"
|
||||||
isDir="false"
|
isDir="false"
|
||||||
coverArt="71381"
|
coverArt="71381"
|
||||||
created="2004-11-08T23:36:11"
|
created="2004-11-08T23:36:11"
|
||||||
@@ -251,7 +252,8 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("getting genres", () => {
|
describe("getting genres", () => {
|
||||||
describe("when there is only 1", () => {
|
describe("when there is only 1", () => {
|
||||||
const genres = ["HipHop"];
|
const genres = ["genre1"];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET
|
mockGET
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
|
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
|
||||||
@@ -265,7 +267,7 @@ describe("Navidrome", () => {
|
|||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.genres());
|
.then((it) => it.genres());
|
||||||
|
|
||||||
expect(result).toEqual(genres.sort());
|
expect(result).toEqual(genres.map(asGenre));
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getGenres`, {
|
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getGenres`, {
|
||||||
params: {
|
params: {
|
||||||
@@ -277,7 +279,7 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when there are many", () => {
|
describe("when there are many", () => {
|
||||||
const genres = ["HipHop", "Rap", "TripHop", "Pop", "Rock"];
|
const genres = ["g1", "g2", "g3", "g3"]
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET
|
mockGET
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
|
.mockImplementationOnce(() => Promise.resolve(ok(PING_OK)))
|
||||||
@@ -291,7 +293,7 @@ describe("Navidrome", () => {
|
|||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.genres());
|
.then((it) => it.genres());
|
||||||
|
|
||||||
expect(result).toEqual(genres.sort());
|
expect(result).toEqual(genres.map(asGenre));
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getGenres`, {
|
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getGenres`, {
|
||||||
params: {
|
params: {
|
||||||
@@ -306,9 +308,9 @@ describe("Navidrome", () => {
|
|||||||
describe("getting an artist", () => {
|
describe("getting an artist", () => {
|
||||||
describe("when the artist exists", () => {
|
describe("when the artist exists", () => {
|
||||||
describe("and has many similar artists", () => {
|
describe("and has many similar artists", () => {
|
||||||
const album1: Album = anAlbum();
|
const album1: Album = anAlbum({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const album2: Album = anAlbum();
|
const album2: Album = anAlbum({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
@@ -372,9 +374,9 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has one similar artists", () => {
|
describe("and has one similar artists", () => {
|
||||||
const album1: Album = anAlbum();
|
const album1: Album = anAlbum({ genre: asGenre("G1") });
|
||||||
|
|
||||||
const album2: Album = anAlbum();
|
const album2: Album = anAlbum({ genre: asGenre("G2") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
@@ -435,9 +437,9 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has no similar artists", () => {
|
describe("and has no similar artists", () => {
|
||||||
const album1: Album = anAlbum();
|
const album1: Album = anAlbum({ genre: asGenre("Jock") });
|
||||||
|
|
||||||
const album2: Album = anAlbum();
|
const album2: Album = anAlbum({ genre: asGenre("Mock") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
@@ -498,9 +500,9 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has dodgy looking artist image uris", () => {
|
describe("and has dodgy looking artist image uris", () => {
|
||||||
const album1: Album = anAlbum();
|
const album1: Album = anAlbum({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const album2: Album = anAlbum();
|
const album2: Album = anAlbum({ genre: asGenre("Flop") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
@@ -561,9 +563,9 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has multiple albums", () => {
|
describe("and has multiple albums", () => {
|
||||||
const album1: Album = anAlbum();
|
const album1: Album = anAlbum({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const album2: Album = anAlbum();
|
const album2: Album = anAlbum({ genre: asGenre("Flop") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
@@ -615,7 +617,7 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has only 1 album", () => {
|
describe("and has only 1 album", () => {
|
||||||
const album: Album = anAlbum();
|
const album: Album = anAlbum({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album],
|
albums: [album],
|
||||||
@@ -842,9 +844,9 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("getting albums", () => {
|
describe("getting albums", () => {
|
||||||
describe("filtering", () => {
|
describe("filtering", () => {
|
||||||
const album1 = anAlbum({ genre: "Pop" });
|
const album1 = anAlbum({ genre: asGenre("Pop") });
|
||||||
const album2 = anAlbum({ genre: "Rock" });
|
const album2 = anAlbum({ genre: asGenre("Rock") });
|
||||||
const album3 = anAlbum({ genre: "Pop" });
|
const album3 = anAlbum({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const artist = anArtist({ albums: [album1, album2, album3] });
|
const artist = anArtist({ albums: [album1, album2, album3] });
|
||||||
|
|
||||||
@@ -894,7 +896,7 @@ describe("Navidrome", () => {
|
|||||||
describe("when the artist has only 1 album", () => {
|
describe("when the artist has only 1 album", () => {
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
name: "one hit wonder",
|
name: "one hit wonder",
|
||||||
albums: [anAlbum()],
|
albums: [anAlbum({ genre: asGenre("Pop") })],
|
||||||
});
|
});
|
||||||
const artists = [artist1];
|
const artists = [artist1];
|
||||||
const albums = artists.flatMap((artist) => artist.albums);
|
const albums = artists.flatMap((artist) => artist.albums);
|
||||||
@@ -974,13 +976,17 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when there are less than 500 albums", () => {
|
describe("when there are less than 500 albums", () => {
|
||||||
|
const genre1 = asGenre("genre1");
|
||||||
|
const genre2 = asGenre("genre2");
|
||||||
|
const genre3 = asGenre("genre3");
|
||||||
|
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
name: "abba",
|
name: "abba",
|
||||||
albums: [anAlbum(), anAlbum(), anAlbum()],
|
albums: [anAlbum({ genre: genre1 }), anAlbum({ genre: genre2 }), anAlbum({ genre: genre3 })],
|
||||||
});
|
});
|
||||||
const artist2 = anArtist({
|
const artist2 = anArtist({
|
||||||
name: "babba",
|
name: "babba",
|
||||||
albums: [anAlbum(), anAlbum(), anAlbum()],
|
albums: [anAlbum({ genre: genre1 }), anAlbum({ genre: genre2 }), anAlbum({ genre: genre3 })],
|
||||||
});
|
});
|
||||||
const artists = [artist1, artist2];
|
const artists = [artist1, artist2];
|
||||||
const albums = artists.flatMap((artist) => artist.albums);
|
const albums = artists.flatMap((artist) => artist.albums);
|
||||||
@@ -1048,7 +1054,7 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("when there are more than 500 albums", () => {
|
describe("when there are more than 500 albums", () => {
|
||||||
const first500Albums = range(500).map((i) =>
|
const first500Albums = range(500).map((i) =>
|
||||||
anAlbum({ name: `album ${i}` })
|
anAlbum({ name: `album ${i}`, genre: asGenre(`genre ${i}`) })
|
||||||
);
|
);
|
||||||
const artist = anArtist({
|
const artist = anArtist({
|
||||||
name: "> 500 albums",
|
name: "> 500 albums",
|
||||||
@@ -1101,15 +1107,17 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("getting an album", () => {
|
describe("getting an album", () => {
|
||||||
describe("when it exists", () => {
|
describe("when it exists", () => {
|
||||||
const album = anAlbum();
|
const genre = asGenre("Pop");
|
||||||
|
|
||||||
|
const album = anAlbum({ genre });
|
||||||
|
|
||||||
const artist = anArtist({ albums: [album] });
|
const artist = anArtist({ albums: [album] });
|
||||||
|
|
||||||
const tracks = [
|
const tracks = [
|
||||||
aTrack({ artist, album }),
|
aTrack({ artist, album, genre }),
|
||||||
aTrack({ artist, album }),
|
aTrack({ artist, album, genre }),
|
||||||
aTrack({ artist, album }),
|
aTrack({ artist, album, genre }),
|
||||||
aTrack({ artist, album }),
|
aTrack({ artist, album, genre }),
|
||||||
];
|
];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -1143,7 +1151,10 @@ describe("Navidrome", () => {
|
|||||||
describe("getting tracks", () => {
|
describe("getting tracks", () => {
|
||||||
describe("for an album", () => {
|
describe("for an album", () => {
|
||||||
describe("when the album has multiple tracks", () => {
|
describe("when the album has multiple tracks", () => {
|
||||||
const album = anAlbum({ id: "album1", name: "Burnin" });
|
const hipHop = asGenre("Hip-Hop")
|
||||||
|
const tripHop = asGenre("Trip-Hop")
|
||||||
|
|
||||||
|
const album = anAlbum({ id: "album1", name: "Burnin", genre: hipHop });
|
||||||
const albumSummary = albumToAlbumSummary(album);
|
const albumSummary = albumToAlbumSummary(album);
|
||||||
|
|
||||||
const artist = anArtist({
|
const artist = anArtist({
|
||||||
@@ -1157,10 +1168,10 @@ describe("Navidrome", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const tracks = [
|
const tracks = [
|
||||||
aTrack({ artist: artistSummary, album: albumSummary }),
|
aTrack({ artist: artistSummary, album: albumSummary, genre: hipHop }),
|
||||||
aTrack({ artist: artistSummary, album: albumSummary }),
|
aTrack({ artist: artistSummary, album: albumSummary, genre: hipHop }),
|
||||||
aTrack({ artist: artistSummary, album: albumSummary }),
|
aTrack({ artist: artistSummary, album: albumSummary, genre: tripHop }),
|
||||||
aTrack({ artist: artistSummary, album: albumSummary }),
|
aTrack({ artist: artistSummary, album: albumSummary, genre: tripHop }),
|
||||||
];
|
];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -1191,7 +1202,9 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when the album has only 1 track", () => {
|
describe("when the album has only 1 track", () => {
|
||||||
const album = anAlbum({ id: "album1", name: "Burnin" });
|
const flipFlop = asGenre("Flip-Flop")
|
||||||
|
|
||||||
|
const album = anAlbum({ id: "album1", name: "Burnin", genre: flipFlop });
|
||||||
const albumSummary = albumToAlbumSummary(album);
|
const albumSummary = albumToAlbumSummary(album);
|
||||||
|
|
||||||
const artist = anArtist({
|
const artist = anArtist({
|
||||||
@@ -1204,7 +1217,7 @@ describe("Navidrome", () => {
|
|||||||
image: NO_IMAGES,
|
image: NO_IMAGES,
|
||||||
};
|
};
|
||||||
|
|
||||||
const tracks = [aTrack({ artist: artistSummary, album: albumSummary })];
|
const tracks = [aTrack({ artist: artistSummary, album: albumSummary, genre: flipFlop })];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET
|
mockGET
|
||||||
@@ -1252,14 +1265,14 @@ describe("Navidrome", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the album", async () => {
|
it("should empty array", async () => {
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.tracks(album.id));
|
.then((it) => it.tracks(album.id));
|
||||||
|
|
||||||
expect(result).toEqual(tracks);
|
expect(result).toEqual([]);
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getAlbum`, {
|
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getAlbum`, {
|
||||||
params: {
|
params: {
|
||||||
@@ -1273,7 +1286,9 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("a single track", () => {
|
describe("a single track", () => {
|
||||||
const album = anAlbum({ id: "album1", name: "Burnin" });
|
const pop = asGenre("Pop")
|
||||||
|
|
||||||
|
const album = anAlbum({ id: "album1", name: "Burnin", genre: pop });
|
||||||
const albumSummary = albumToAlbumSummary(album);
|
const albumSummary = albumToAlbumSummary(album);
|
||||||
|
|
||||||
const artist = anArtist({
|
const artist = anArtist({
|
||||||
@@ -1286,7 +1301,7 @@ describe("Navidrome", () => {
|
|||||||
image: NO_IMAGES,
|
image: NO_IMAGES,
|
||||||
};
|
};
|
||||||
|
|
||||||
const track = aTrack({ artist: artistSummary, album: albumSummary });
|
const track = aTrack({ artist: artistSummary, album: albumSummary, genre: pop });
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET
|
mockGET
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ import {
|
|||||||
anArtist,
|
anArtist,
|
||||||
anAlbum,
|
anAlbum,
|
||||||
aTrack,
|
aTrack,
|
||||||
|
POP,
|
||||||
|
ROCK,
|
||||||
|
PUNK, TRIP_HOP
|
||||||
} from "./builders";
|
} from "./builders";
|
||||||
import { InMemoryMusicService } from "./in_memory_music_service";
|
import { InMemoryMusicService } from "./in_memory_music_service";
|
||||||
import supersoap from "./supersoap";
|
import supersoap from "./supersoap";
|
||||||
@@ -187,7 +190,11 @@ describe("track", () => {
|
|||||||
name: "great song",
|
name: "great song",
|
||||||
duration: randomInt(1000),
|
duration: randomInt(1000),
|
||||||
number: randomInt(100),
|
number: randomInt(100),
|
||||||
album: anAlbum({ name: "great album", id: uuid(), genre: "some genre" }),
|
album: anAlbum({
|
||||||
|
name: "great album",
|
||||||
|
id: uuid(),
|
||||||
|
genre: { id: "genre101", name: "some genre" },
|
||||||
|
}),
|
||||||
artist: anArtist({ name: "great artist", id: uuid() }),
|
artist: anArtist({ name: "great artist", id: uuid() }),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -206,8 +213,8 @@ describe("track", () => {
|
|||||||
artist: someTrack.artist.name,
|
artist: someTrack.artist.name,
|
||||||
artistId: someTrack.artist.id,
|
artistId: someTrack.artist.id,
|
||||||
duration: someTrack.duration,
|
duration: someTrack.duration,
|
||||||
genre: someTrack.album.genre,
|
genre: someTrack.album.genre?.name,
|
||||||
// genreId
|
genreId: someTrack.album.genre?.id,
|
||||||
trackNumber: someTrack.number,
|
trackNumber: someTrack.number,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -559,17 +566,20 @@ describe("api", () => {
|
|||||||
|
|
||||||
describe("asking for a genres", () => {
|
describe("asking for a genres", () => {
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
albums: [anAlbum({ genre: "Pop" }), anAlbum({ genre: "Rock" })],
|
albums: [
|
||||||
|
anAlbum({ genre: POP }),
|
||||||
|
anAlbum({ genre: ROCK }),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
const artist2 = anArtist({
|
const artist2 = anArtist({
|
||||||
albums: [
|
albums: [
|
||||||
anAlbum({ genre: "Trip-Hop" }),
|
anAlbum({ genre: TRIP_HOP }),
|
||||||
anAlbum({ genre: "Punk" }),
|
anAlbum({ genre: PUNK }),
|
||||||
anAlbum({ genre: "Pop" }),
|
anAlbum({ genre: POP }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const expectedGenres = ["Pop", "Punk", "Rock", "Trip-Hop"];
|
const expectedGenres = [POP, PUNK, ROCK, TRIP_HOP];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
musicService.hasArtists(artist1, artist2);
|
musicService.hasArtists(artist1, artist2);
|
||||||
@@ -586,8 +596,8 @@ describe("api", () => {
|
|||||||
getMetadataResult({
|
getMetadataResult({
|
||||||
mediaCollection: expectedGenres.map((genre) => ({
|
mediaCollection: expectedGenres.map((genre) => ({
|
||||||
itemType: "container",
|
itemType: "container",
|
||||||
id: `genre:${genre}`,
|
id: `genre:${genre.id}`,
|
||||||
title: genre,
|
title: genre.name,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
total: expectedGenres.length,
|
total: expectedGenres.length,
|
||||||
@@ -605,10 +615,10 @@ describe("api", () => {
|
|||||||
});
|
});
|
||||||
expect(result[0]).toEqual(
|
expect(result[0]).toEqual(
|
||||||
getMetadataResult({
|
getMetadataResult({
|
||||||
mediaCollection: ["Punk", "Rock"].map((genre) => ({
|
mediaCollection: [PUNK, ROCK].map((genre) => ({
|
||||||
itemType: "container",
|
itemType: "container",
|
||||||
id: `genre:${genre}`,
|
id: `genre:${genre.id}`,
|
||||||
title: genre,
|
title: genre.name,
|
||||||
})),
|
})),
|
||||||
index: 1,
|
index: 1,
|
||||||
total: expectedGenres.length,
|
total: expectedGenres.length,
|
||||||
|
|||||||
Reference in New Issue
Block a user