Files
dredgenews/app/Services/FeedFetcher.php
T
dredgy 439139a057
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Done
2026-07-04 23:09:00 +10:00

71 lines
2.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\Article;
use App\Models\Feed;
use SimplePie\SimplePie;
class FeedFetcher
{
public function __construct(private OgImageResolver $ogResolver) {}
public function fetch(Feed $feed): void
{
$simplepie = new SimplePie();
$simplepie->set_feed_url($feed->url);
$simplepie->enable_cache(false); // you're doing your own caching in the DB
$simplepie->init();
if ($simplepie->error()) {
report(new \RuntimeException("Feed fetch failed for {$feed->url}: {$simplepie->error()}"));
return;
}
foreach ($simplepie->get_items() as $item) {
$imageUrl = $this->extractImage($item);
$attributes = [
'title' => strip_tags($item->get_title()),
'description' => strip_tags($item->get_description() ?? ''),
'link' => $item->get_permalink(),
'author' => $item->get_author()?->get_name(),
'published_at' => $item->get_date('Y-m-d H:i:s'),
];
if ($imageUrl) {
$attributes['image_url'] = $imageUrl;
}
$article = Article::updateOrCreate(
['feed_id' => $feed->id, 'guid' => $item->get_id()],
$attributes
);
if (! $article->image_url && ! $article->image_checked_at) {
$og = $this->ogResolver->resolve($article->link);
$article->update([
'image_url' => $og,
'image_checked_at' => now(),
]);
}
}
}
private function extractImage($item): ?string
{
$mediaContents = $item->get_item_tags('http://search.yahoo.com/mrss/', 'content');
if (empty($mediaContents)) {
$enclosure = $item->get_enclosure();
$link = $enclosure?->get_thumbnail() ?? $enclosure?->get_link();
return $link ? html_entity_decode($link) : null;
}
usort($mediaContents, fn ($a, $b) =>
(int) ($b['attribs']['']['width'] ?? 0) <=> (int) ($a['attribs']['']['width'] ?? 0)
);
return $mediaContents[0]['attribs']['']['url'] ?? null;
}
}