From a30b9bb649d6e10a5d7c2feb73376669cf23ef68 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Thu, 24 Nov 2022 23:31:33 +0300 Subject: rework favicon storage to use DiskCache --- classes/rssutils.php | 104 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 34 deletions(-) (limited to 'classes/rssutils.php') diff --git a/classes/rssutils.php b/classes/rssutils.php index 22859791d..a8c56295b 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -37,20 +37,29 @@ class RSSUtils { $pdo = Db::pdo(); $sth = $pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ?"); - // check icon files once every Config::get(Config::CACHE_MAX_DAYS) days - $icon_files = array_filter(glob(Config::get(Config::ICONS_DIR) . "/*.ico"), - fn(string $f) => filemtime($f) < time() - 86400 * Config::get(Config::CACHE_MAX_DAYS)); + $cache = new DiskCache('feed-icons'); - foreach ($icon_files as $icon) { - $feed_id = basename($icon, ".ico"); + if ($cache->is_writable()) { + $dh = opendir($cache->get_full_path("")); - $sth->execute([$feed_id]); + if ($dh) { + while (($icon = readdir($dh)) !== false) { + if ($cache->get_mtime($icon) < time() - 86400 * Config::get(Config::CACHE_MAX_DAYS)) { - if ($sth->fetch()) { - @touch($icon); - } else { - Debug::log("Removing orphaned feed icon: $icon"); - unlink($icon); + $sth->execute([(int)$icon]); + + if ($sth->fetch()) { + $cache->put($icon, $cache->get($icon)); + } else { + $icon_path = $cache->get_full_path($icon); + + Debug::log("Removing orphaned feed icon: $icon_path"); + unlink($icon); + } + } + } + + closedir($dh); } } } @@ -480,10 +489,10 @@ class RSSUtils { } // cache vanilla feed data for re-use - if ($feed_data && !$feed_obj->auth_pass && !$feed_obj->auth_login && is_writable(Config::get(Config::CACHE_DIR) . "/feeds")) { + if ($feed_data && !$feed_obj->auth_pass && !$feed_obj->auth_login && $cache->is_writable()) { $new_rss_hash = sha1($feed_data); - if ($new_rss_hash != $rss_hash && $cache->is_writable()) { + if ($new_rss_hash != $rss_hash) { Debug::log("saving to local cache: $cache_filename", Debug::LOG_VERBOSE); $cache->put($cache_filename, $feed_data); } @@ -593,21 +602,28 @@ class RSSUtils { if ($feed_obj->favicon_needs_check || $force_refetch) { - /* terrible hack: if we crash on floicon shit here, we won't check - * the icon avgcolor again (unless the icon got updated) */ + // restrict update attempts to once per 12h + $feed_obj->favicon_last_checked = Db::NOW(); + $feed_obj->save(); - $favicon_file = Config::get(Config::ICONS_DIR) . "/$feed.ico"; - $favicon_modified = file_exists($favicon_file) ? filemtime($favicon_file) : -1; + $favicon_cache = new DiskCache('feed-icons'); + $favicon_modified = $favicon_cache->exists($feed) ? $favicon_cache->get_mtime($feed) : -1; + + // don't try to redownload custom favicons if (!$feed_obj->favicon_is_custom) { Debug::log("favicon: trying to update favicon...", Debug::LOG_VERBOSE); self::update_favicon($feed_obj->site_url, $feed); - if ((file_exists($favicon_file) ? filemtime($favicon_file) : -1) > $favicon_modified) + if (!$favicon_cache->exists($feed) || $favicon_cache->get_mtime($feed) > $favicon_modified) { $feed_obj->favicon_avg_color = null; + $feed_obj->save(); + } } - if (is_readable($favicon_file) && function_exists("imagecreatefromstring") && empty($feed_obj->favicon_avg_color)) { + /* terrible hack: if we crash on floicon shit here, we won't check + * the icon avgcolor again (unless icon got updated) */ + if (file_exists($favicon_cache->get_full_path($feed)) && function_exists("imagecreatefromstring") && empty($feed_obj->favicon_avg_color)) { require_once "colors.php"; Debug::log("favicon: trying to calculate average color...", Debug::LOG_VERBOSE); @@ -615,13 +631,13 @@ class RSSUtils { $feed_obj->favicon_avg_color = 'fail'; $feed_obj->save(); - $feed_obj->favicon_avg_color = \Colors\calculate_avg_color($favicon_file); + $feed_obj->favicon_avg_color = \Colors\calculate_avg_color($favicon_cache->get_full_path($feed)); $feed_obj->save(); Debug::log("favicon: avg color: {$feed_obj->favicon_avg_color}", Debug::LOG_VERBOSE); } else if ($feed_obj->favicon_avg_color == 'fail') { - Debug::log("floicon failed $favicon_file, not trying to recalculate avg color", Debug::LOG_VERBOSE); + Debug::log("floicon failed on $feed, not trying to recalculate avg color", Debug::LOG_VERBOSE); } } @@ -1672,10 +1688,35 @@ class RSSUtils { $tmph->run_hooks(PluginHost::HOOK_HOUSE_KEEPING); } + /** migrates favicons from legacy storage in feed-icons/ to cache/feed-icons/using new naming (sans .ico suffix) */ + static function migrate_feed_icons() : void { + $old_dir = Config::get(Config::ICONS_DIR); + $new_dir = Config::get(Config::CACHE_DIR) . '/feed-icons'; + + $dh = opendir($old_dir); + + if ($dh) { + while (($old_filename = readdir($dh)) !== false) { + if (strpos($old_filename, ".ico") !== false) { + $new_filename = str_replace(".ico", "", $old_filename); + $old_full_path = "$old_dir/$old_filename"; + $new_full_path = "$new_dir/$new_filename"; + + if (is_file($old_full_path) && !file_exists($new_full_path)) { + rename($old_full_path, $new_full_path); + } + } + } + + closedir($dh); + } + } + static function housekeeping_common(): void { $cache = new DiskCache(""); $cache->expire_all(); + self::migrate_feed_icons(); self::expire_lock_files(); self::expire_error_log(); self::expire_feed_archive(); @@ -1693,8 +1734,6 @@ class RSSUtils { * @return false|string */ static function update_favicon(string $site_url, int $feed) { - $icon_file = Config::get(Config::ICONS_DIR) . "/$feed.ico"; - $favicon_urls = self::get_favicon_urls($site_url); if (count($favicon_urls) == 0) { @@ -1749,21 +1788,18 @@ class RSSUtils { break; } - Debug::log("favicon: $favicon_url looks valid, saving to $icon_file", Debug::LOG_VERBOSE); - - $fp = @fopen($icon_file, "w"); - - if ($fp) { + $favicon_cache = new DiskCache('feed-icons'); - fwrite($fp, $contents); - fclose($fp); - chmod($icon_file, 0644); - clearstatcache(); + if ($favicon_cache->is_writable()) { + Debug::log("favicon: $favicon_url looks valid, saving to cache", Debug::LOG_VERBOSE); - return $icon_file; + // we deal with this manually + if (!$favicon_cache->exists(".no-auto-expiry")) + $favicon_cache->put(".no-auto-expiry", ""); + return $favicon_cache->put((string)$feed, $contents); } else { - Debug::log("favicon: failed to open $icon_file for writing", Debug::LOG_VERBOSE); + Debug::log("favicon: $favicon_url skipping, local cache is not writable", Debug::LOG_VERBOSE); } } -- cgit v1.2.3