201 lines
5.3 KiB
TypeScript
201 lines
5.3 KiB
TypeScript
/**
|
|
* Service pour interagir avec l'API pmosource générique
|
|
*
|
|
* Ce service utilise uniquement l'API REST définie dans pmosource::api
|
|
* et ne dépend d'aucune implémentation spécifique (comme pmoparadise)
|
|
*/
|
|
|
|
const API_BASE = '/api/sources'
|
|
|
|
// Types correspondant aux structures de l'API pmosource
|
|
|
|
export interface SourceInfo {
|
|
id: string
|
|
name: string
|
|
supports_fifo: boolean
|
|
capabilities: SourceCapabilities
|
|
}
|
|
|
|
export interface SourceCapabilities {
|
|
supports_search: boolean
|
|
supports_favorites: boolean
|
|
supports_playlists: boolean
|
|
supports_user_content: boolean
|
|
supports_high_res_audio: boolean
|
|
max_sample_rate: number | null
|
|
supports_multiple_formats: boolean
|
|
supports_advanced_search: boolean
|
|
supports_pagination: boolean
|
|
}
|
|
|
|
export interface SourcesList {
|
|
count: number
|
|
sources: SourceInfo[]
|
|
}
|
|
|
|
export interface BrowseContainer {
|
|
id: string
|
|
parent_id: string
|
|
title: string
|
|
class: string
|
|
child_count: string | null
|
|
restricted: string | null
|
|
}
|
|
|
|
export interface BrowseItemResource {
|
|
url: string
|
|
protocol_info: string
|
|
duration: string | null
|
|
}
|
|
|
|
export interface BrowseItem {
|
|
id: string
|
|
parent_id: string
|
|
title: string
|
|
class: string
|
|
artist: string | null
|
|
album: string | null
|
|
creator: string | null
|
|
album_art: string | null
|
|
resources: BrowseItemResource[]
|
|
}
|
|
|
|
export interface BrowseResponse {
|
|
object_id: string
|
|
containers: BrowseContainer[]
|
|
items: BrowseItem[]
|
|
returned_containers: number
|
|
returned_items: number
|
|
total: number
|
|
update_id: number
|
|
}
|
|
|
|
export interface ResolveUriResponse {
|
|
object_id: string
|
|
uri: string
|
|
}
|
|
|
|
export interface SourceRootContainer {
|
|
id: string
|
|
parent_id: string
|
|
title: string
|
|
class: string
|
|
child_count: string | null
|
|
searchable: string | null
|
|
}
|
|
|
|
/**
|
|
* Liste toutes les sources musicales enregistrées
|
|
*/
|
|
export async function listSources(): Promise<SourcesList> {
|
|
const response = await fetch(`${API_BASE}`)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to list sources: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Récupère les informations d'une source spécifique
|
|
*/
|
|
export async function getSource(sourceId: string): Promise<SourceInfo> {
|
|
const response = await fetch(`${API_BASE}/${sourceId}`)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to get source: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Récupère le container racine d'une source
|
|
*/
|
|
export async function getSourceRoot(sourceId: string): Promise<SourceRootContainer> {
|
|
const response = await fetch(`${API_BASE}/${sourceId}/root`)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to get source root: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Parcourt un container d'une source
|
|
*
|
|
* @param sourceId - ID de la source
|
|
* @param objectId - ID de l'objet à parcourir (optionnel, par défaut utilise la racine)
|
|
* @param startingIndex - Index de départ pour la pagination
|
|
* @param requestedCount - Nombre d'éléments demandés
|
|
*/
|
|
export async function browseSource(
|
|
sourceId: string,
|
|
objectId?: string,
|
|
startingIndex?: number,
|
|
requestedCount?: number
|
|
): Promise<BrowseResponse> {
|
|
const params = new URLSearchParams()
|
|
if (objectId) params.set('object_id', objectId)
|
|
if (startingIndex !== undefined) params.set('starting_index', startingIndex.toString())
|
|
if (requestedCount !== undefined) params.set('requested_count', requestedCount.toString())
|
|
|
|
const url = `${API_BASE}/${sourceId}/browse?${params.toString()}`
|
|
const response = await fetch(url)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to browse source: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Résout l'URI réelle d'un objet (pour le streaming)
|
|
*
|
|
* @param sourceId - ID de la source
|
|
* @param objectId - ID de l'objet à résoudre
|
|
*/
|
|
export async function resolveUri(sourceId: string, objectId: string): Promise<ResolveUriResponse> {
|
|
const params = new URLSearchParams({ object_id: objectId })
|
|
const url = `${API_BASE}/${sourceId}/resolve?${params.toString()}`
|
|
|
|
const response = await fetch(url)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to resolve URI: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Récupère l'URL de l'image par défaut d'une source
|
|
*
|
|
* @param sourceId - ID de la source
|
|
* @returns L'URL de l'image
|
|
*/
|
|
export function getSourceImageUrl(sourceId: string): string {
|
|
return `${API_BASE}/${sourceId}/image`
|
|
}
|
|
|
|
/**
|
|
* Récupère les capacités d'une source
|
|
*/
|
|
export async function getSourceCapabilities(sourceId: string): Promise<SourceCapabilities> {
|
|
const response = await fetch(`${API_BASE}/${sourceId}/capabilities`)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to get source capabilities: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|
|
|
|
/**
|
|
* Récupère les métadonnées détaillées d'un item spécifique
|
|
*
|
|
* @param sourceId - ID de la source
|
|
* @param objectId - ID de l'item à récupérer
|
|
*/
|
|
export async function getItem(sourceId: string, objectId: string): Promise<BrowseItem> {
|
|
const params = new URLSearchParams({ object_id: objectId })
|
|
const url = `${API_BASE}/${sourceId}/item?${params.toString()}`
|
|
|
|
const response = await fetch(url)
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to get item: ${response.status} ${response.statusText}`)
|
|
}
|
|
return response.json()
|
|
}
|