$Message
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { api } from '../index';
|
||||
import { toTrack, type RawTrack } from '../mappers';
|
||||
import type { RadioResult } from '../types';
|
||||
|
||||
// Radio + similarity (§6.5). Stateless: the client passes the seed + already-
|
||||
// queued ids and pulls more as the queue drains.
|
||||
|
||||
interface RawRadioTrack {
|
||||
track: RawTrack;
|
||||
reason: string;
|
||||
}
|
||||
interface RawRadio {
|
||||
source: string;
|
||||
tracks: RawRadioTrack[];
|
||||
}
|
||||
interface RawSimilar {
|
||||
source: string;
|
||||
tracks: RawTrack[];
|
||||
}
|
||||
|
||||
export interface RadioArgs {
|
||||
seedTrackId?: string;
|
||||
fromLikes?: boolean;
|
||||
exploration?: number;
|
||||
count?: number;
|
||||
excludeIds?: string[];
|
||||
}
|
||||
|
||||
const toBody = (a: RadioArgs) => ({
|
||||
seed_track_id: a.seedTrackId ?? null,
|
||||
from_likes: a.fromLikes ?? false,
|
||||
exploration: a.exploration ?? 0.25,
|
||||
count: a.count ?? 20,
|
||||
exclude_ids: a.excludeIds ?? [],
|
||||
});
|
||||
|
||||
const toRadio = (r: RawRadio): RadioResult => ({
|
||||
source: r.source,
|
||||
picks: r.tracks.map((x) => ({ track: toTrack(x.track), reason: x.reason })),
|
||||
});
|
||||
|
||||
export const radioApi = api.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
startRadio: build.mutation<RadioResult, RadioArgs>({
|
||||
query: (a) => ({ url: '/radio', method: 'POST', body: toBody(a) }),
|
||||
transformResponse: (r: RawRadio) => toRadio(r),
|
||||
}),
|
||||
radioNext: build.mutation<RadioResult, RadioArgs>({
|
||||
query: (a) => ({ url: '/radio/next', method: 'POST', body: toBody(a) }),
|
||||
transformResponse: (r: RawRadio) => toRadio(r),
|
||||
}),
|
||||
getSimilarTracks: build.query<RadioResult, string>({
|
||||
query: (trackId) => `/tracks/${trackId}/similar`,
|
||||
transformResponse: (r: RawSimilar) => ({
|
||||
source: r.source,
|
||||
picks: r.tracks.map((t) => ({ track: toTrack(t), reason: 'similar' })),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
overrideExisting: false,
|
||||
});
|
||||
|
||||
export const {
|
||||
useStartRadioMutation,
|
||||
useRadioNextMutation,
|
||||
useGetSimilarTracksQuery,
|
||||
} = radioApi;
|
||||
@@ -192,6 +192,20 @@ export interface DuplicateGroup {
|
||||
tracks: Track[];
|
||||
}
|
||||
|
||||
/** One radio/similarity pick with its reason code (§6.5). `reason`: `ml` |
|
||||
* `similar` | `from_likes` | `discover` — localized client-side. */
|
||||
export interface RadioPick {
|
||||
track: Track;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/** Radio / similar response: where the picks came from + the picks. `source`:
|
||||
* `ml` (recommender) or `metadata` (fallback heuristics). */
|
||||
export interface RadioResult {
|
||||
source: string;
|
||||
picks: RadioPick[];
|
||||
}
|
||||
|
||||
/** Cached lyrics for a track (§6.7). `not_found` is a normal state, not an
|
||||
* error. `synced` is raw LRC (client parses timestamps); `plain` is fallback. */
|
||||
export interface Lyrics {
|
||||
|
||||
Reference in New Issue
Block a user