import axios from 'axios' const api = axios.create({ baseURL: '/api', headers: { 'Content-Type': 'application/json', }, }) api.interceptors.request.use((config) => { const token = localStorage.getItem('vynl_token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) api.interceptors.response.use( (response) => response, (error) => { if (error.response?.status === 401) { localStorage.removeItem('vynl_token') window.location.href = '/login' } return Promise.reject(error) } ) // Types export interface TokenResponse { access_token: string token_type: string } export interface UserResponse { id: string email: string name: string is_pro: boolean daily_recommendations_remaining: number spotify_connected: boolean created_at: string } export interface PlaylistResponse { id: string name: string source: string track_count: number image_url: string | null created_at: string } export interface TrackItem { id: string title: string artist: string album: string duration_ms: number image_url: string | null spotify_url: string | null } export interface TasteProfile { top_genres: { name: string; weight: number }[] energy: number mood: number danceability: number acousticness: number instrumentalness: number } export interface PlaylistDetailResponse { id: string name: string source: string track_count: number image_url: string | null tracks: TrackItem[] taste_profile: TasteProfile | null created_at: string } export interface SpotifyPlaylistItem { id: string name: string track_count: number image_url: string | null owner: string } export interface RecommendationItem { id: string title: string artist: string album: string image_url: string | null spotify_url: string | null bandcamp_url: string | null youtube_url: string | null reason: string saved: boolean disliked: boolean created_at: string } export interface RecommendationResponse { recommendations: RecommendationItem[] remaining_this_week: number } export interface TasteProfileArtist { name: string track_count: number genre: string } export interface TasteProfileResponse { genre_breakdown: { name: string; percentage: number }[] audio_features: { energy: number danceability: number valence: number acousticness: number avg_tempo: number } personality: { label: string description: string icon: string } top_artists: TasteProfileArtist[] track_count: number playlist_count: number } // Auth export const register = (email: string, name: string, password: string) => api.post('/auth/register', { email, name, password }).then((r) => r.data) export const login = (email: string, password: string) => api.post('/auth/login', { email, password }).then((r) => r.data) export const getMe = () => api.get('/auth/me').then((r) => r.data) // Spotify OAuth export const getSpotifyAuthUrl = () => api.get<{ url: string; state: string }>('/auth/spotify/authorize').then((r) => r.data) export const spotifyCallback = (code: string) => api.post('/auth/spotify/callback', { code }).then((r) => r.data) // Playlists export const getPlaylists = () => api.get('/playlists').then((r) => r.data) export const getPlaylist = (id: string) => api.get(`/playlists/${id}`).then((r) => r.data) export const deletePlaylist = (id: string) => api.delete(`/playlists/${id}`).then((r) => r.data) // Spotify Import export const getSpotifyPlaylists = () => api.get('/spotify/playlists').then((r) => r.data) export const importSpotifyPlaylist = (playlistId: string) => api.post('/spotify/import', { playlist_id: playlistId }).then((r) => r.data) // Recommendations export const generateRecommendations = ( playlistId?: string, query?: string, bandcampMode?: boolean, mode?: string, adventurousness?: number, exclude?: string, count?: number, ) => api.post('/recommendations/generate', { playlist_id: playlistId, query, bandcamp_mode: bandcampMode || false, mode: mode || 'discover', adventurousness: adventurousness ?? 3, exclude: exclude || undefined, count: count ?? 5, }).then((r) => r.data) export const getRecommendationHistory = () => api.get('/recommendations/history').then((r) => r.data) export const getSavedRecommendations = () => api.get('/recommendations/saved').then((r) => r.data) export const toggleSaveRecommendation = (id: string) => api.post<{ saved: boolean }>(`/recommendations/${id}/save`).then((r) => r.data) export const dislikeRecommendation = (id: string) => api.post<{ disliked: boolean }>(`/recommendations/${id}/dislike`).then((r) => r.data) // YouTube Music Import export interface YouTubeTrackResult { title: string artist: string album: string | null youtube_id: string | null image_url: string | null } export const importYouTubePlaylist = (url: string) => api.post('/youtube-music/import', { url }).then((r) => r.data) export const searchYouTubeMusic = (query: string) => api.post('/youtube-music/search', { query }).then((r) => r.data) // Last.fm Import export interface LastfmPreviewTrack { title: string artist: string playcount: number image_url: string | null } export interface LastfmPreviewResponse { display_name: string track_count: number sample_tracks: LastfmPreviewTrack[] } export const previewLastfm = (username: string) => api.get('/lastfm/preview', { params: { username } }).then((r) => r.data) export const importLastfm = (username: string, period: string) => api.post('/lastfm/import', { username, period }).then((r) => r.data) // Manual Import (paste songs) export const importPastedSongs = (name: string, text: string) => api.post('/import/paste', { name, text }).then((r) => r.data) // Billing export interface BillingStatusResponse { is_pro: boolean subscription_status: string | null current_period_end: number | null } export const createCheckout = () => api.post<{ url: string }>('/billing/create-checkout').then((r) => r.data) export const createBillingPortal = () => api.post<{ url: string }>('/billing/portal').then((r) => r.data) export const getBillingStatus = () => api.get('/billing/status').then((r) => r.data) // Bandcamp export interface BandcampResult { title: string artist: string art_url: string | null bandcamp_url: string item_type: string } export interface BandcampEmbed { embed_url: string title: string artist: string art_url: string | null } export async function searchBandcamp(query: string, type: string = 't'): Promise { const { data } = await api.get('/bandcamp/search', { params: { q: query, type } }) return data } export async function getBandcampEmbed(url: string): Promise { const { data } = await api.get('/bandcamp/embed', { params: { url } }) return data } // Playlist Fix export interface OutlierTrack { track_number: number artist: string title: string reason: string } export interface ReplacementTrack { title: string artist: string album: string | null reason: string } export interface PlaylistFixResponse { playlist_vibe: string outliers: OutlierTrack[] replacements: ReplacementTrack[] } export const fixPlaylist = (playlistId: string) => api.post(`/playlists/${playlistId}/fix`).then((r) => r.data) // Taste Profile export const getTasteProfile = () => api.get('/profile/taste').then((r) => r.data) export default api