import { BUrn } from "./burn"; import { taskEither as TE } from "fp-ts"; export type Credentials = { username: string; password: string }; export type AuthSuccess = { serviceToken: string; userId: string; nickname: string; }; export class AuthFailure extends Error { constructor(message: string) { super(message); } }; export type ArtistSummary = { id: string | undefined; name: string; image: BUrn | undefined; }; export type SimilarArtist = ArtistSummary & { inLibrary: boolean }; // todo: maybe is should be artist.summary rather than an artist also being a summary? export type Artist = Pick & { albums: AlbumSummary[]; similarArtists: SimilarArtist[] }; export type AlbumSummary = { id: string; name: string; year: string | undefined; genre: Genre | undefined; coverArt: BUrn | undefined; artistName: string | undefined; artistId: string | undefined; }; export type Album = Pick & { tracks: Track[] }; export type Genre = { name: string; id: string; } export type Year = { year: string; } export type Rating = { love: boolean; stars: number; } export type Encoding = { player: string, mimeType: string } export type TrackSummary = { id: string; name: string; encoding: Encoding, duration: number; number: number | undefined; genre: Genre | undefined; coverArt: BUrn | undefined; artist: ArtistSummary; rating: Rating; } export type Track = TrackSummary & { album: AlbumSummary; }; export type RadioStation = { id: string, name: string, url: string, homePage?: string } export type Paging = { _index: number; _count: number; }; export type Result = { results: T[]; total: number; }; export function slice2({ _index, _count }: Paging) { return (things: T[]): [T[], number] => [ things.slice(_index, _index + _count), things.length, ]; } export const asResult = ([results, total]: [T[], number]) => ({ results, total, }); export type ArtistQuery = Paging; export type AlbumQueryType = 'alphabeticalByArtist' | 'alphabeticalByName' | 'byGenre' | 'byYear' | 'random' | 'recentlyPlayed' | 'mostPlayed' | 'recentlyAdded' | 'favourited' | 'starred'; export type AlbumQuery = Paging & { type: AlbumQueryType; genre?: string; fromYear?: string; toYear?: string; }; export const artistToArtistSummary = (it: Artist): ArtistSummary => ({ id: it.id, name: it.name, image: it.image }); export const albumToAlbumSummary = (it: Album): AlbumSummary => ({ id: it.id, name: it.name, year: it.year, genre: it.genre, artistName: it.artistName, artistId: it.artistId, coverArt: it.coverArt }); export const trackToTrackSummary = (it: Track): TrackSummary => ({ id: it.id, name: it.name, encoding: it.encoding, duration: it.duration, number: it.number, genre: it.genre, coverArt: it.coverArt, artist: it.artist, rating: it.rating }); export const playlistToPlaylistSummary = (it: Playlist): PlaylistSummary => ({ id: it.id, name: it.name, coverArt: it.coverArt }) export type StreamingHeader = "content-type" | "content-length" | "content-range" | "accept-ranges"; export type TrackStream = { status: number; headers: Record; stream: any; }; export type CoverArt = { contentType: string; data: Buffer; } export type PlaylistSummary = { id: string, name: string, coverArt?: BUrn | undefined } export type Playlist = PlaylistSummary & { entries: Track[] } export const range = (size: number) => [...Array(size).keys()]; export const asArtistAlbumPairs = (artists: Artist[]): [Artist, Album][] => artists.flatMap((artist) => artist.albums.map((album) => [artist, album] as [Artist, Album]) ); export interface MusicService { generateToken(credentials: Credentials): TE.TaskEither; refreshToken(serviceToken: string): TE.TaskEither; login(serviceToken: string): Promise; } export interface MusicLibrary { artists(q: ArtistQuery): Promise>; artist(id: string): Promise; albums(q: AlbumQuery): Promise>; album(id: string): Promise; track(trackId: string): Promise; genres(): Promise; years(): Promise; stream({ trackId, range, }: { trackId: string; range: string | undefined; }): Promise; rate(trackId: string, rating: Rating): Promise; coverArt(coverArtURN: BUrn, size?: number): Promise; nowPlaying(id: string): Promise scrobble(id: string): Promise searchArtists(query: string): Promise; searchAlbums(query: string): Promise; searchTracks(query: string): Promise; playlists(): Promise; playlist(id: string): Promise; createPlaylist(name: string): Promise deletePlaylist(id: string): Promise addToPlaylist(playlistId: string, trackId: string): Promise removeFromPlaylist(playlistId: string, indicies: number[]): Promise similarSongs(id: string): Promise; topSongs(artistId: string): Promise; radioStation(id: string): Promise radioStations(): Promise }