rework favicon storage to use DiskCache

This commit is contained in:
Andrew Dolgov
2022-11-24 23:31:33 +03:00
parent be6bc72a74
commit a30b9bb649
10 changed files with 167 additions and 74 deletions

View File

@@ -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);
$favicon_cache = new DiskCache('feed-icons');
$fp = @fopen($icon_file, "w");
if ($favicon_cache->is_writable()) {
Debug::log("favicon: $favicon_url looks valid, saving to cache", Debug::LOG_VERBOSE);
if ($fp) {
fwrite($fp, $contents);
fclose($fp);
chmod($icon_file, 0644);
clearstatcache();
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);
}
}