*/ 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 $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; } }