aimusic-attribution / server /hfSpacesClient.ts
emresar's picture
Upload folder using huggingface_hub
6678fa1 verified
/**
* HuggingFace Spaces API Client
*
* Calls the ML API hosted on HuggingFace Spaces instead of local Python.
* This enables deployment on platforms without Python support.
*
* Set HF_SPACES_URL environment variable to your Space URL:
* HF_SPACES_URL=https://username-space-name.hf.space
*/
import FormData from 'form-data';
import fs from 'fs';
import path from 'path';
const HF_SPACES_URL = process.env.HF_SPACES_URL;
export function isHfSpacesEnabled(): boolean {
return !!HF_SPACES_URL;
}
export function getSpacesUrl(): string | undefined {
return HF_SPACES_URL;
}
interface EmbeddingResult {
success: boolean;
embedding?: number[];
dimension?: number;
error?: string;
}
interface ChunkResult {
success: boolean;
total_duration?: number;
chunk_count?: number;
chunks?: Array<{
start_time: number;
end_time: number;
embedding: number[];
}>;
error?: string;
}
interface SearchResult {
matches: Array<{
score: number;
trackId: string;
title: string;
artist: string;
distinctiveness?: number;
}>;
mean_similarity?: number;
}
/**
* Extract MERT embedding via HuggingFace Spaces API
*/
export async function extractEmbedding(audioPath: string): Promise<EmbeddingResult> {
if (!HF_SPACES_URL) {
return { success: false, error: "HF_SPACES_URL not configured" };
}
try {
const formData = new FormData();
formData.append('audio_file', fs.createReadStream(audioPath));
const response = await fetch(`${HF_SPACES_URL}/api/extract_embedding`, {
method: 'POST',
body: formData as any,
});
const text = await response.text();
return JSON.parse(text);
} catch (error) {
return { success: false, error: String(error) };
}
}
/**
* Extract chunk embeddings via HuggingFace Spaces API
*/
export async function extractChunks(
audioPath: string,
chunkDuration: number = 10.0,
overlap: number = 5.0
): Promise<ChunkResult> {
if (!HF_SPACES_URL) {
return { success: false, error: "HF_SPACES_URL not configured" };
}
try {
const formData = new FormData();
formData.append('audio_file', fs.createReadStream(audioPath));
formData.append('chunk_duration', String(chunkDuration));
formData.append('overlap', String(overlap));
const response = await fetch(`${HF_SPACES_URL}/api/extract_chunks`, {
method: 'POST',
body: formData as any,
});
const text = await response.text();
return JSON.parse(text);
} catch (error) {
return { success: false, error: String(error) };
}
}
/**
* Search for similar tracks via HuggingFace Spaces API
*/
export async function searchSimilar(
queryEmbedding: number[],
indexData: { entries: Array<{ trackId: string; title: string; artist: string; embedding: number[] }> },
k: number = 5
): Promise<SearchResult> {
if (!HF_SPACES_URL) {
return { matches: [] };
}
try {
const formData = new FormData();
formData.append('query_embedding_json', JSON.stringify(queryEmbedding));
formData.append('index_json', JSON.stringify(indexData));
formData.append('k', String(k));
const response = await fetch(`${HF_SPACES_URL}/api/search`, {
method: 'POST',
body: formData as any,
});
const text = await response.text();
return JSON.parse(text);
} catch (error) {
return { matches: [] };
}
}
/**
* Check if HuggingFace Spaces API is healthy
*/
export async function checkHealth(): Promise<{ ok: boolean; details?: any }> {
if (!HF_SPACES_URL) {
return { ok: false, details: { error: "HF_SPACES_URL not configured" } };
}
try {
const response = await fetch(`${HF_SPACES_URL}/api/health`, {
method: 'POST',
});
const text = await response.text();
const data = JSON.parse(text);
return { ok: data.status === 'ok', details: data };
} catch (error) {
return { ok: false, details: { error: String(error) } };
}
}