Files
muzick/frontend/src/services/historyService.ts
T
kami bfe22745bc feat(discovery): acquire recommendations that keep their names
Acquisition ran yt-dlp without --embed-metadata, so every download
arrived untagged. The scanner then stored the video id as the title and
"Unknown Artist" as the artist, the vetted-candidate tag check rejected
the mismatch, and all 18 acquired tracks were hidden and retired.

- Pass --embed-metadata so downloads carry real tags.
- Let a scan take fallback title/artist from the candidate, for sources
  that still ship untagged files.
- Install Deno alongside yt-dlp: YouTube guards some formats with a JS
  challenge yt-dlp must execute, and no other runtime is enabled.
- Dedupe candidates by artist and title. The (source, external_id) key
  misses the same song reaching us under two Deezer release ids.

Also carries the in-flight discovery work this builds on: the
Recommendations page replacing Discover, the discovery source service,
and the acquisition spec tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 18:43:58 +04:00

39 lines
1.2 KiB
TypeScript

import api from './api';
import type { FeedbackAction, HistoryEntry } from '../types';
export const historyService = {
// POST /api/history { trackId, completed?, batchId?, listenedMs? } -> { historyId }
async recordPlay(
trackId: string,
completed?: boolean,
batchId?: string,
listenedMs?: number
): Promise<{ historyId: string }> {
const res = await api.post<{ historyId: string }>('/history', {
trackId,
completed,
batchId,
listenedMs,
});
return res.data;
},
// POST /api/history/skip { trackId } -> { status }
async skip(trackId: string): Promise<{ status: string }> {
const res = await api.post<{ status: string }>('/history/skip', { trackId });
return res.data;
},
// GET /api/history -> recent play history (Track + playback metadata)
async list(): Promise<HistoryEntry[]> {
const res = await api.get<HistoryEntry[]>('/history');
return res.data;
},
// POST /api/feedback { trackId, action } -> { status }
async feedback(trackId: string, action: FeedbackAction): Promise<{ status: string }> {
const res = await api.post<{ status: string }>('/feedback', { trackId, action });
return res.data;
},
};