*/
private function process_article(array $row, int $width) : array {
$need_saving = false;
$article = isset($row['headline']) ? $row['headline'] : $row['article'];
if ($width <= 0)
return $article;
$doc = new DOMDocument();
if (@$doc->loadHTML('' . $article["content"])) {
$xpath = new DOMXPath($doc);
$imgs = $xpath->query("//img[@src]");
foreach ($imgs as $img) {
$orig_src = $img->getAttribute("src");
$new_src = $this->rewrite_url_if_needed($orig_src, $width, $article['link']);
if ($new_src != $orig_src) {
$need_saving = true;
$parent = $img->parentNode;
if ($parent && $parent->tagName != "a") {
$img_link = $doc->createElement("a");
$img_link->setAttribute("href", $orig_src);
$img_link->setAttribute("target", "_blank");
$parent->replaceChild($img_link, $img);
$img_link->appendChild($img);
}
$img->setAttribute("src", $new_src);
$img->removeAttribute("srcset");
}
}
$vids = $xpath->query("//video[@poster]");
foreach ($vids as $vid) {
$new_poster = $this->rewrite_url_if_needed($vid->getAttribute("poster"), $width, $article['link'], true);
if ($new_poster != $vid->getAttribute("poster")) {
$vid->setAttribute("poster", $new_poster);
$need_saving = true;
}
}
$psrcs = $xpath->query("//picture/source[@src]");
foreach ($psrcs as $src) {
$new_src = $this->rewrite_url_if_needed($src->getAttribute("src"), $width, $article['link']);
if ($new_src != $src->getAttribute("src")) {
$src->setAttribute("src", $new_src);
$need_saving = true;
}
}
}
if (isset($article["attachments"]) && is_array($article["attachments"])) {
$tmp =& $article["attachments"];
for ($i = 0; $i < count($tmp); $i++) {
if (preg_match("/image/", $tmp[$i]["content_type"])) {
$tmp[$i]["content_url"] = $this->rewrite_url_if_needed($tmp[$i]["content_url"], $width, $article['link']);
}
}
}
if ($need_saving)
$article["content"] = $doc->saveHTML();
return $article;
}
function hook_article_image($enclosures, $content, $site_url, $article) {
$width = (int) clean($_REQUEST["resize_width"] ?? 0);
$article = $this->process_article([
"headline" =>
[
"content" => $content,
"link" => $article['link']
]
], $width);
return ["", "", $article["content"]];
}
function hook_enclosure_entry($enc, $id, $rv) {
$force_width = (int) $this->host->profile_get($this, "force_width", 0);
if (!isset($this->article_link_cache[$id])) {
$article = ORM::for_table('ttrss_entries')
->join('ttrss_user_entries', ['ref_id', '=', 'id'], 'ue')
->where('ue.owner_uid', $_SESSION['uid'])
->find_one((int)$id);
$this->article_link_cache[$id] = $article->link;
}
$enc["content_url"] = $this->rewrite_url_if_needed($enc["content_url"], $force_width,
($this->article_link_cache[$id] ?? ""));
return $enc;
}
function hook_article_filter($article) {
$widths = array_unique(
array_map("intval",
$this->host->get_array($this, "prepare_widths")));
if (count($widths) == 0)
return $article;
$site_url = $article["feed"]["site_url"] ?? "";
$doc = new DOMDocument();
if (@$doc->loadHTML('' . $article["content"])) {
$xpath = new DOMXPath($doc);
$imgs = $xpath->query("//img[@src]");
foreach ($imgs as $img) {
$this->prepare_thumbnails(
UrlHelper::rewrite_relative($site_url, $img->getAttribute("src")),
$widths, false);
}
$vids = $xpath->query("//video[@poster]");
foreach ($vids as $vid) {
$this->prepare_thumbnails(
UrlHelper::rewrite_relative($site_url, $vid->getAttribute("poster")),
$widths, false);
}
$posters = $xpath->query("//picture/source[@src]");
foreach ($posters as $poster) {
$this->prepare_thumbnails(
UrlHelper::rewrite_relative($site_url, $poster->getAttribute("src")),
$widths, true);
}
/** @var FeedEnclosure $enc */
foreach ($article["enclosures"] as $enc) {
if (preg_match("/image/", $enc->type)) {
$this->prepare_thumbnails(
UrlHelper::rewrite_relative($site_url, $enc->link),
$widths, false);
}
}
}
return $article;
}
function hook_render_article_cdm($row) {
$force_width = (int) $this->host->profile_get($this, "force_width", 0);
return $this->process_article(["article" => $row], $force_width);
}
function hook_render_article($row) {
$force_width = (int) $this->host->profile_get($this, "force_width", 0);
return $this->process_article(["article" => $row], $force_width);
}
function hook_render_article_api($row) {
$width = (int) clean($_REQUEST["resize_width"] ?? 0);
return $this->process_article($row, $width);
}
// https://stackoverflow.com/questions/280658/can-i-detect-animated-gifs-using-php-and-gd
function is_animated_gif(string $filename) : bool {
$raw = file_get_contents($filename);
if ($raw) {
$offset = 0;
$frames = 0;
while ($frames < 2 && $offset <= 500000) {
$where1 = strpos($raw, "\x00\x21\xF9\x04", $offset);
if ($where1 === false) {
break;
} else {
$offset = $where1 + 1;
$where2 = strpos($raw, "\x00\x2C", $offset);
if ($where2 === false) {
break;
} else {
if ($where1 + 8 == $where2) {
$frames++;
}
$offset = $where2 + 1;
}
}
}
return $frames > 1;
}
return false;
}
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
$force_width = (int) $this->host->profile_get($this, "force_width", 0);
$prepare_widths = implode(", ", $this->host->get_array($this, "prepare_widths"));
$domain_blacklist = implode(", ", $this->host->get_array($this, "domain_blacklist", self::DEFAULT_DOMAIN_BLACKLIST));
$quality = (int) $this->host->profile_get($this, "quality", self::DEFAULT_QUALITY);
?>
host->set($this, "prepare_widths", $prepare_widths);
$this->host->set($this, "domain_blacklist", $domain_blacklist);
$this->host->profile_set($this, "force_width", $force_width);
$this->host->profile_set($this, "quality", $quality);
echo $this->T_sprintf("Configuration has been saved.");
}
function api_version() {
return 2;
}
}