143 lines
5.3 KiB
PHP
143 lines
5.3 KiB
PHP
<?php
|
|||
|
|
/**
|
||
|
|
* Intégration Funkwhale : récupération de morceaux aléatoires via l'API.
|
||
|
|
*
|
||
|
|
* Extrait de includes/config.php (ARC-1) — chargé par ce dernier.
|
||
|
|
* Dépend de lib/http.php (client cURL) et lib/format.php (formatDuration).
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Récupère des morceaux aléatoires depuis Funkwhale via l'API
|
||
|
|
*
|
||
|
|
* @param string $funkwhaleUrl URL de l'instance Funkwhale
|
||
|
|
* @param int $count Nombre de morceaux à récupérer
|
||
|
|
* @return array Liste des morceaux formatés
|
||
|
|
*/
|
||
|
|
function getFunkwhaleTracks($funkwhaleUrl = null, $count = null) {
|
||
|
|
if (!defined('FUNKWHALE_ENABLED') || !FUNKWHALE_ENABLED) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$funkwhaleUrl = $funkwhaleUrl ?? FUNKWHALE_URL;
|
||
|
|
$count = $count ?? FUNKWHALE_TRACKS_COUNT;
|
||
|
|
|
||
|
|
// Validation de l'URL Funkwhale pour prévenir SSRF
|
||
|
|
if (!isValidRemoteUrl($funkwhaleUrl)) {
|
||
|
|
error_log('SECURITY: Invalid Funkwhale URL detected: ' . $funkwhaleUrl);
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clé de cache - on récupère un grand nombre de morceaux pour le cache
|
||
|
|
$cacheKey = 'funkwhale_' . md5($funkwhaleUrl);
|
||
|
|
$cacheFetchSize = 50; // Nombre de morceaux à mettre en cache
|
||
|
|
|
||
|
|
// Vérifier le cache
|
||
|
|
$allTracks = null;
|
||
|
|
if (defined('CACHE_ENABLED') && CACHE_ENABLED) {
|
||
|
|
$allTracks = getFromCache($cacheKey);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Si pas de cache, récupérer depuis l'API
|
||
|
|
if ($allTracks === null) {
|
||
|
|
try {
|
||
|
|
// Construire l'URL de l'API en ordre aléatoire ; le filtrage local
|
||
|
|
// est fait ensuite côté PHP (comparaison de domaine)
|
||
|
|
$apiUrl = rtrim($funkwhaleUrl, '/') . '/api/v1/tracks?ordering=random&page_size=' . $cacheFetchSize . '&scope=all';
|
||
|
|
|
||
|
|
// Récupérer les morceaux
|
||
|
|
$response = httpGet($apiUrl, ['timeout' => 10]);
|
||
|
|
$jsonContent = $response['body'];
|
||
|
|
|
||
|
|
if ($response['code'] !== 200 || !$jsonContent) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Parser le JSON
|
||
|
|
$data = json_decode($jsonContent, true);
|
||
|
|
if (!$data || !isset($data['results'])) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$allTracks = [];
|
||
|
|
|
||
|
|
// Extraire le domaine de l'instance locale pour filtrer
|
||
|
|
$localDomain = parse_url($funkwhaleUrl, PHP_URL_HOST);
|
||
|
|
|
||
|
|
// Parcourir les morceaux et filtrer pour garder uniquement les morceaux locaux
|
||
|
|
foreach ($data['results'] as $item) {
|
||
|
|
// Vérifier si le morceau est local en comparant le domaine
|
||
|
|
// Les morceaux fédérés ont généralement un domaine différent dans leur URL
|
||
|
|
$isLocal = true;
|
||
|
|
|
||
|
|
// Vérifier via l'URL de l'artiste ou de l'album
|
||
|
|
if (isset($item['artist']['channel'])) {
|
||
|
|
$artistDomain = parse_url($item['artist']['channel'], PHP_URL_HOST);
|
||
|
|
if ($artistDomain && $artistDomain !== $localDomain) {
|
||
|
|
$isLocal = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ou vérifier via le champ is_local si disponible
|
||
|
|
if (isset($item['is_local']) && !$item['is_local']) {
|
||
|
|
$isLocal = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Ne garder que les morceaux locaux
|
||
|
|
if ($isLocal) {
|
||
|
|
// Récupérer l'URL d'écoute depuis l'API (peut être relative)
|
||
|
|
$listenUrl = $item['listen_url'] ?? '';
|
||
|
|
|
||
|
|
// Si l'URL est relative, la transformer en URL absolue
|
||
|
|
if (!empty($listenUrl) && strpos($listenUrl, 'http') !== 0) {
|
||
|
|
$listenUrl = rtrim($funkwhaleUrl, '/') . $listenUrl;
|
||
|
|
}
|
||
|
|
|
||
|
|
$track = [
|
||
|
|
'title' => $item['title'] ?? '',
|
||
|
|
'artist' => $item['artist']['name'] ?? 'Artiste inconnu',
|
||
|
|
'album' => $item['album']['title'] ?? '',
|
||
|
|
'cover' => $item['album']['cover']['urls']['medium_square_crop'] ?? '',
|
||
|
|
'duration' => $item['uploads'][0]['duration'] ?? 0,
|
||
|
|
'link' => rtrim($funkwhaleUrl, '/') . '/library/tracks/' . ($item['id'] ?? ''),
|
||
|
|
'trackId' => $item['id'] ?? 0,
|
||
|
|
'audioUrl' => $listenUrl,
|
||
|
|
];
|
||
|
|
|
||
|
|
// Formater la durée
|
||
|
|
if ($track['duration']) {
|
||
|
|
$track['formattedDuration'] = formatDuration($track['duration']);
|
||
|
|
} else {
|
||
|
|
$track['formattedDuration'] = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$allTracks[] = $track;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mettre en cache tous les morceaux locaux
|
||
|
|
if (defined('CACHE_ENABLED') && CACHE_ENABLED) {
|
||
|
|
saveToCache($cacheKey, $allTracks);
|
||
|
|
}
|
||
|
|
|
||
|
|
} catch (Exception $e) {
|
||
|
|
error_log('Erreur lors de la récupération des morceaux Funkwhale: ' . $e->getMessage());
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Si pas assez de morceaux, retourner ce qu'on a
|
||
|
|
if (empty($allTracks)) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sélectionner aléatoirement $count morceaux parmi tous les morceaux en cache
|
||
|
|
$trackCount = count($allTracks);
|
||
|
|
if ($trackCount <= $count) {
|
||
|
|
return $allTracks;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mélanger et prendre les N premiers
|
||
|
|
shuffle($allTracks);
|
||
|
|
return array_slice($allTracks, 0, $count);
|
||
|
|
}
|