veza/apps/web/src/services/trackService.ts

134 lines
3.7 KiB
TypeScript

import { apiClient } from '@/services/api/client';
import { Track, PaginatedResponse, TrackDTO } from '@/types/api';
import { trackSchema } from '@/schemas/apiSchemas';
import { uploadService, UploadMetadata } from './uploadService';
const env = {
API_URL: import.meta.env.VITE_API_URL || '',
};
const mapTrackDTO = (dto: TrackDTO): Track =>
({
id: dto.id,
title: dto.title,
artist:
typeof dto.artist === 'object' ? (dto.artist as any).name : dto.artist,
album: dto.album || 'Unknown',
duration: dto.duration_formatted || '0:00',
durationSec: dto.duration || 0,
genre: dto.genre || 'None',
year: dto.year || new Date().getFullYear(),
coverUrl: dto.cover_art_path
? `${env.API_URL}${dto.cover_art_path}`
: undefined,
cover_art_path: dto.cover_art_path,
filePath: dto.file_path,
file_path: dto.file_path,
file_size: dto.file_size || 0,
format: dto.format || 'mp3',
bitrate: dto.bitrate || 0,
sample_rate: dto.sample_rate || 0,
play_count: dto.play_count || 0,
like_count: dto.like_count || 0,
plays: dto.play_count || 0,
likes: dto.like_count || 0,
created_at: dto.created_at,
updated_at: dto.updated_at,
creator_id: dto.creator_id,
is_public: dto.is_public ?? true,
status: dto.status as any,
stream_status: (dto.stream_status as any) || 'pending',
stream_manifest_url: dto.stream_manifest_url,
}) as Track;
export const trackService = {
list: async (params?: {
user_id?: string;
artist?: string;
genre?: string;
page?: number;
limit?: number;
sort_by?: string;
order?: 'asc' | 'desc';
query?: string;
search?: string;
}) => {
const response = await apiClient.get<PaginatedResponse<TrackDTO>>(
'/tracks',
{
params: {
...params,
query: params?.query || params?.search,
},
},
);
const data = response.data;
const items = data.items || [];
return {
tracks: items.map(mapTrackDTO),
pagination: {
total: data.total,
page: data.page,
limit: data.limit,
total_pages: data.total_pages,
has_next: data.has_next,
has_prev: data.has_prev,
},
};
},
search: async (query: string) => {
const response = await apiClient.get<TrackDTO[]>('/tracks/search', {
params: { query },
});
return { tracks: response.data.map(mapTrackDTO) };
},
get: async (id: string) => {
const response = await apiClient.get<TrackDTO>(`/tracks/${id}`, {
validateSchema: trackSchema,
} as any);
return { track: mapTrackDTO(response.data) };
},
update: async (id: string, data: Partial<Track>) => {
const payload = {
title: data.title,
artist: data.artist,
album: data.album,
genre: data.genre,
is_public: data.is_public,
};
const response = await apiClient.put<TrackDTO>(`/tracks/${id}`, payload, {
validateSchema: trackSchema,
} as any);
return { track: mapTrackDTO(response.data) };
},
delete: async (id: string) => apiClient.delete(`/tracks/${id}`),
like: async (id: string) => apiClient.post(`/tracks/${id}/like`),
unlike: async (id: string) => apiClient.delete(`/tracks/${id}/like`),
recordPlay: async (id: string) => apiClient.post(`/tracks/${id}/play`),
download: async (id: string) => {
const response = await apiClient.get(`/tracks/${id}/download`, {
responseType: 'blob',
});
return response.data;
},
upload: async (
file: File,
metadata: UploadMetadata,
onProgress?: (progress: number) => void,
) => {
return await uploadService.uploadTrack(file, metadata, onProgress);
},
getStatus: async (trackId: string) => {
return await uploadService.getTrackStatus(trackId);
},
};