BloomFeed Upgrade v0.21

This commit is contained in:
Esteban
2026-07-04 03:07:54 +02:00
parent d73c25cbbb
commit 28ba35da13
36 changed files with 1981 additions and 274 deletions
+104 -3
View File
@@ -2,28 +2,129 @@
namespace App\Services;
use App\Models\Setting;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;
class FluxCatalogService
{
/**
* Retourne le catalogue de flux embarqué (resources/data/bloomflux.json).
* La liste est versionnée avec le code : la mettre à jour = éditer le fichier et commit.
* Retourne le catalogue complet : le fichier embarqué (resources/data/bloomflux.json)
* fusionné avec les sources externes configurées (URLs de fichiers bloomflux.json
* hébergés dans d'autres dépôts). Dédoublonné par URL de flux, l'embarqué gagne.
*
* @return list<array{title: string, url: string, category: string, lang: string, description: ?string}>
*/
public function getCatalog(): array
{
$feeds = $this->loadEmbedded();
foreach (Setting::catalogSources() as $sourceUrl) {
$feeds = array_merge($feeds, $this->loadRemoteSource($sourceUrl));
}
$seen = [];
$unique = [];
foreach ($feeds as $feed) {
if (! isset($seen[$feed['url']])) {
$seen[$feed['url']] = true;
$unique[] = $feed;
}
}
return $unique;
}
/**
* Valide une source distante et retourne le nombre de flux qu'elle contient,
* ou null si elle est injoignable ou invalide.
*/
public function validateSource(string $sourceUrl): ?int
{
$feeds = $this->fetchSource($sourceUrl);
return $feeds === null ? null : count($feeds);
}
private function loadEmbedded(): array
{
try {
$raw = file_get_contents(resource_path('data/bloomflux.json'));
$data = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
return $data['feeds'] ?? [];
return $this->normalize($data['feeds'] ?? []);
} catch (Throwable $e) {
Log::warning('Catalogue bloomflux.json illisible', ['error' => $e->getMessage()]);
return [];
}
}
/**
* Charge une source externe : réseau si joignable (et met le cache à jour),
* sinon dernière copie en cache.
*/
private function loadRemoteSource(string $sourceUrl): array
{
$cacheKey = 'bloomflux.source.'.md5($sourceUrl);
$feeds = $this->fetchSource($sourceUrl);
if ($feeds !== null) {
Cache::forever($cacheKey, $feeds);
return $feeds;
}
Log::warning('Source de catalogue injoignable, utilisation du cache', ['source' => $sourceUrl]);
return Cache::get($cacheKey, []);
}
private function fetchSource(string $sourceUrl): ?array
{
try {
$response = Http::timeout(6)
->withHeaders(['User-Agent' => 'BloomFeed/1.0'])
->get($sourceUrl);
if ($response->failed()) {
return null;
}
$data = json_decode($response->body(), true, 512, JSON_THROW_ON_ERROR);
if (! isset($data['feeds']) || ! is_array($data['feeds'])) {
return null;
}
return $this->normalize($data['feeds']);
} catch (Throwable) {
return null;
}
}
private function normalize(array $feeds): array
{
$clean = [];
foreach ($feeds as $feed) {
if (! is_array($feed) || empty($feed['title']) || empty($feed['url'])) {
continue;
}
$clean[] = [
'title' => (string) $feed['title'],
'url' => (string) $feed['url'],
'category' => (string) ($feed['category'] ?? 'Divers'),
'lang' => strtolower((string) ($feed['lang'] ?? 'en')),
'description' => isset($feed['description']) ? (string) $feed['description'] : null,
];
}
return $clean;
}
}