Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/jangernert/FeedReader.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrendan Long <self@brendanlong.com>2017-10-20 02:24:36 +0300
committerBrendan Long <self@brendanlong.com>2017-10-20 02:37:21 +0300
commit1da28f5d2d38fc83b9d95476367a67a8a37ddfa3 (patch)
treecc43917f577f11a3781afdda4d33179cab3c682d
parent610a467cc27d844fca9b0d0f3d77aa9477c2488c (diff)
Use a map of promises for loading favicons
This makes it so: - We only try to download each icon once - Once an icon is loaded, we can return it immediately from the map - Favicon loading is fully async - Removed ReloadFavicon signal since we currently don't ever reload them I merged some of the code so we only have getIcon() and downloadFavicon()
-rw-r--r--src/Backend/Backend.vala1
-rw-r--r--src/FavIconManager.vala198
-rw-r--r--src/FeedReader.vala8
-rw-r--r--src/Widgets/ArticleList/ArticleList.vala5
-rw-r--r--src/Widgets/ArticleList/ArticleListBox.vala11
-rw-r--r--src/Widgets/ArticleRow.vala30
-rw-r--r--src/Widgets/ColumnView.vala6
-rw-r--r--src/Widgets/FeedList.vala13
-rw-r--r--src/Widgets/FeedRow.vala32
9 files changed, 103 insertions, 201 deletions
diff --git a/src/Backend/Backend.vala b/src/Backend/Backend.vala
index ef681a32..3bdbb6bb 100644
--- a/src/Backend/Backend.vala
+++ b/src/Backend/Backend.vala
@@ -34,7 +34,6 @@ namespace FeedReader {
public signal void newFeedList();
public signal void refreshFeedListCounter();
public signal void updateArticleList();
- public signal void reloadFavIcons();
public signal void showArticleListOverlay();
public signal void setOffline();
public signal void setOnline();
diff --git a/src/FavIconManager.vala b/src/FavIconManager.vala
index 7693b553..0fa4a4dc 100644
--- a/src/FavIconManager.vala
+++ b/src/FavIconManager.vala
@@ -15,11 +15,9 @@
public class FeedReader.FavIconManager : GLib.Object {
- private Gee.HashMap<string, Gdk.Pixbuf> m_map;
+ private Gee.Map<string, Gee.Future<Gdk.Pixbuf?>> m_map = new Gee.HashMap<string, Gee.Future<Gdk.Pixbuf?>>();
private static FavIconManager? m_cache = null;
- public signal void ReloadFavIcon(Feed feed);
-
public static FavIconManager get_default()
{
if(m_cache == null)
@@ -30,76 +28,54 @@ public class FeedReader.FavIconManager : GLib.Object {
private FavIconManager()
{
- m_map = new Gee.HashMap<string, Gdk.Pixbuf>();
}
- private async void load(Feed feed, bool firstTry = true)
+ public async Gdk.Pixbuf? getIcon(Feed feed)
{
- var fileName = GLib.Base64.encode(feed.getFeedID().data) + ".ico";
try
{
- var file = File.new_for_path(GLib.Environment.get_user_data_dir() + "/feedreader/data/feed_icons/" + fileName);
- var stream = yield file.read_async();
- var pixbuf = yield new Gdk.Pixbuf.from_stream_async(stream);
- stream.close();
- if(pixbuf.get_height() <= 1 && pixbuf.get_width() <= 1)
+ var feed_id = feed.getFeedID();
+ var future = m_map.get(feed_id);
+ if(future == null)
{
- Logger.warning(@"FavIconManager: $fileName is too small");
- return;
- }
-
- pixbuf = pixbuf.scale_simple(24, 24, Gdk.InterpType.BILINEAR);
- m_map.set(feed.getFeedID(), pixbuf);
- ReloadFavIcon(feed);
- }
- catch (IOError.NOT_FOUND e)
- {
- //Logger.debug(@"FavIconManager: Icon $fileName does not exist");
-
- if(!firstTry)
- return;
+ var promise = new Gee.Promise<Gdk.Pixbuf?>();
+ try
+ {
+ future = promise.future;
+ m_map.set(feed_id, future);
- bool success = yield downloadFavIcon(feed);
- if(success)
- yield load(feed, false);
- }
- catch(Gdk.PixbufError.UNKNOWN_TYPE e)
- {
- Logger.warning(@"FavIconManager.load: Icon $fileName is an unknown type");
- }
- catch(Error e)
- {
- Logger.error(@"FavIconManager.load: $fileName: %s".printf(e.message));
- }
- }
+ var stream = yield downloadFavIcon(feed);
+ if(stream == null)
+ return null;
- private bool hasIcon(Feed feed)
- {
- if(m_map == null)
- {
- m_map = new Gee.HashMap<string, Gdk.Pixbuf>();
- return false;
- }
+ var pixbuf = yield new Gdk.Pixbuf.from_stream_async(stream);
+ stream.close();
- return m_map.has_key(feed.getFeedID());
- }
+ if(pixbuf.get_height() <= 1 && pixbuf.get_width() <= 1)
+ {
+ Logger.warning(@"FavIconManager: Icon for feed %s is too small".printf(feed.getTitle()));
+ return null;
+ }
+ pixbuf = pixbuf.scale_simple(24, 24, Gdk.InterpType.BILINEAR);
- public async Gdk.Pixbuf? getIcon(Feed feed, bool firstTry = true)
- {
- if(hasIcon(feed))
- {
- return m_map.get(feed.getFeedID()).copy();
+ promise.set_value(pixbuf);
+ }
+ finally
+ {
+ if(!future.ready)
+ promise.set_value(null);
+ }
+ }
+ return yield future.wait_async();
}
- else if(firstTry)
+ catch(Error e)
{
- yield load(feed);
- return yield getIcon(feed, false);
+ Logger.error("FavIconManager.getIcon: %s".printf(e.message));
+ return null;
}
-
- return null;
}
- private async bool downloadFavIcon(Feed feed, string? hint_url = null, GLib.Cancellable? cancellable = null, string icon_path = GLib.Environment.get_user_data_dir() + "/feedreader/data/feed_icons/")
+ private async InputStream? downloadFavIcon(Feed feed, GLib.Cancellable? cancellable = null, string icon_path = GLib.Environment.get_user_data_dir() + "/feedreader/data/feed_icons/") throws GLib.Error
{
string filename_prefix = icon_path + feed.getFeedFileName();
string local_filename = @"$filename_prefix.ico";
@@ -109,7 +85,7 @@ public class FeedReader.FavIconManager : GLib.Object {
DateTime? expires = metadata.expires;
if(cancellable != null && cancellable.is_cancelled())
- return false;
+ return null;
var now = new DateTime.now_utc();
if(expires != null)
@@ -117,7 +93,15 @@ public class FeedReader.FavIconManager : GLib.Object {
if(expires.to_unix() > now.to_unix())
{
Logger.debug("Favicon for %s is valid until %s, skipping this time".printf(feed.getTitle(), expires.to_string()));
- return yield Utils.file_exists(local_filename, FileType.REGULAR);
+ var file = File.new_for_path(local_filename);
+ try
+ {
+ return yield file.read_async();
+ }
+ catch(FileError.NOENT e)
+ {
+ return null;
+ }
}
}
@@ -126,9 +110,6 @@ public class FeedReader.FavIconManager : GLib.Object {
var obvious_icons = new Gee.ArrayList<string>();
- if(hint_url != null)
- obvious_icons.add(hint_url);
-
if(feed.getIconURL() != null)
obvious_icons.add(feed.getIconURL());
@@ -147,20 +128,20 @@ public class FeedReader.FavIconManager : GLib.Object {
obvious_icons.add(icon_url);
}
-
// Try to find one of those icons
foreach(var url in obvious_icons)
{
- if(yield downloadIcon(feed, url, cancellable, icon_path))
- return true;
+ var stream = yield downloadIcon(feed, url, cancellable, icon_path);
+ if(stream != null)
+ return stream;
if(cancellable != null && cancellable.is_cancelled())
- return false;
+ return null;
}
// If all else fails, download html and parse to find location of favicon
if(siteURL == null)
- return false;
+ return null;
var message_html = new Soup.Message("GET", siteURL);
if(Settings.tweaks().get_boolean("do-not-track"))
@@ -175,7 +156,7 @@ public class FeedReader.FavIconManager : GLib.Object {
catch (Error e)
{
Logger.warning(@"Request for $siteURL failed: " + e.message);
- return false;
+ return null;
}
if(html != null && message_html.status_code == 200)
{
@@ -185,43 +166,47 @@ public class FeedReader.FavIconManager : GLib.Object {
if(doc == null)
{
Logger.debug(@"Utils.downloadFavIcon: parsing html on $siteURL failed");
- return false;
+ return null;
}
- // check for <link rel="icon">
- var xpath = grabberUtils.getURL(doc, "//link[@rel='icon']");
+ try
+ {
+ // check for <link rel="icon">
+ var xpath = grabberUtils.getURL(doc, "//link[@rel='icon']");
- if(xpath == null)
- // check for <link rel="shortcut icon">
- xpath = grabberUtils.getURL(doc, "//link[@rel='shortcut icon']");
+ if(xpath == null)
+ // check for <link rel="shortcut icon">
+ xpath = grabberUtils.getURL(doc, "//link[@rel='shortcut icon']");
- if(xpath == null)
- // check for <link rel="apple-touch-icon">
- xpath = grabberUtils.getURL(doc, "//link[@rel='apple-touch-icon']");
+ if(xpath == null)
+ // check for <link rel="apple-touch-icon">
+ xpath = grabberUtils.getURL(doc, "//link[@rel='apple-touch-icon']");
- if(xpath != null)
+ if(xpath != null)
+ {
+ xpath = grabberUtils.completeURL(xpath, siteURL);
+ return yield downloadIcon(feed, xpath, cancellable, icon_path);
+ }
+ }
+ finally
{
- xpath = grabberUtils.completeURL(xpath, siteURL);
- if(yield downloadIcon(feed, xpath, cancellable, icon_path))
- return true;
+ delete doc;
}
-
- delete doc;
}
- return false;
+ return null;
}
- private async bool downloadIcon(Feed feed, string? icon_url, Cancellable? cancellable, string icon_path = GLib.Environment.get_user_data_dir() + "/feedreader/data/feed_icons/")
+ private async InputStream? downloadIcon(Feed feed, string? icon_url, Cancellable? cancellable, string icon_path = GLib.Environment.get_user_data_dir() + "/feedreader/data/feed_icons/") throws GLib.Error
{
if(icon_url == "" || icon_url == null || GLib.Uri.parse_scheme(icon_url) == null)
{
Logger.warning(@"Utils.downloadIcon: icon_url not valid $icon_url");
- return false;
+ return null;
}
if(!yield Utils.ensure_path(icon_path))
- return false;
+ return null;
string filename_prefix = icon_path + feed.getFeedFileName();
string local_filename = @"$filename_prefix.ico";
@@ -250,46 +235,29 @@ public class FeedReader.FavIconManager : GLib.Object {
catch (Error e)
{
Logger.error(@"Request for $icon_url failed: " + e.message);
- return false;
+ return null;
}
var status = message.status_code;
if(status == 304)
{
- return true;
+ var file = File.new_for_path(local_filename);
+ return yield file.read_async();
}
else if(status == 404 || data == null)
{
- return false;
+ return null;
}
else if(status == 200)
{
var local_file = File.new_for_path(local_filename);
- uint8[]? local_data = null;
try
{
- uint8[] contents;
- yield local_file.load_contents_async(null, out contents, null);
- local_data = contents;
+ yield local_file.replace_contents_async(data, null, false, FileCreateFlags.NONE, null, null);
}
- catch(IOError.NOT_FOUND e){}
catch(Error e)
{
- Logger.error(@"Error reading icon $local_filename: %s".printf(e.message));
- }
-
- if(local_data == null
- ||(local_data != null && data != local_data))
- {
- try
- {
- yield local_file.replace_contents_async(data, null, false, FileCreateFlags.NONE, null, null);
- FileUtils.set_data(local_filename, data);
- }
- catch(Error e)
- {
- Logger.error("Error writing icon: %s".printf(e.message));
- return false;
- }
+ Logger.error("Error writing icon: %s".printf(e.message));
+ return null;
}
metadata.etag = message.response_headers.get_one("ETag");
@@ -303,7 +271,6 @@ public class FeedReader.FavIconManager : GLib.Object {
var parts = header.split("=");
if(parts.length < 2 || parts[0] != "max-age")
continue;
- Logger.debug(parts[1]);
var seconds = int64.parse(parts[1]);
var expires = new DateTime.now_utc();
expires.add_seconds(seconds);
@@ -313,9 +280,10 @@ public class FeedReader.FavIconManager : GLib.Object {
}
metadata.last_modified = message.response_headers.get_one("Last-Modified");
yield metadata.save_to_file_async(metadata_filename);
- return true;
+ return new MemoryInputStream.from_data(data);
}
+
Logger.warning(@"Could not download icon for feed: %s $icon_url, got response code $status".printf(feed.getFeedID()));
- return false;
+ return null;
}
}
diff --git a/src/FeedReader.vala b/src/FeedReader.vala
index 5fdd7af2..29ea63ed 100644
--- a/src/FeedReader.vala
+++ b/src/FeedReader.vala
@@ -84,14 +84,6 @@ namespace FeedReader {
});
});
- FeedReaderBackend.get_default().reloadFavIcons.connect(() => {
- GLib.Idle.add(() => {
- Logger.debug("FeedReader: reloadFavIcons");
- ColumnView.get_default().reloadFavIcons();
- return GLib.Source.REMOVE;
- });
- });
-
FeedReaderBackend.get_default().updateArticleList.connect(() => {
GLib.Idle.add(() => {
Logger.debug("FeedReader: updateArticleList");
diff --git a/src/Widgets/ArticleList/ArticleList.vala b/src/Widgets/ArticleList/ArticleList.vala
index 48d14909..8e174ccb 100644
--- a/src/Widgets/ArticleList/ArticleList.vala
+++ b/src/Widgets/ArticleList/ArticleList.vala
@@ -779,9 +779,4 @@ public class FeedReader.ArticleList : Gtk.Overlay {
{
m_currentList.emptyList();
}
-
- public void reloadFavIcons()
- {
- m_currentList.reloadFavIcons();
- }
}
diff --git a/src/Widgets/ArticleList/ArticleListBox.vala b/src/Widgets/ArticleList/ArticleListBox.vala
index 1920b81a..67444bcf 100644
--- a/src/Widgets/ArticleList/ArticleListBox.vala
+++ b/src/Widgets/ArticleList/ArticleListBox.vala
@@ -662,15 +662,4 @@ public class FeedReader.ArticleListBox : Gtk.ListBox {
addRow(ArticleListBalance.NONE, false, false);
return true;
}
-
- public void reloadFavIcons()
- {
- var children = this.get_children();
- foreach(var row in children)
- {
- var tmpRow = row as ArticleRow;
- if(tmpRow != null)
- tmpRow.reloadFavIcon.begin();
- }
- }
}
diff --git a/src/Widgets/ArticleRow.vala b/src/Widgets/ArticleRow.vala
index 667ce3b3..bf31b1f2 100644
--- a/src/Widgets/ArticleRow.vala
+++ b/src/Widgets/ArticleRow.vala
@@ -137,12 +137,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow {
m_marked_eventbox.leave_notify_event.connect(markedIconLeave);
m_marked_eventbox.button_press_event.connect(markedIconClicked);
-
m_icon = createFavIcon();
- FavIconManager.get_default().ReloadFavIcon.connect(feed => {
- if(m_article.getFeedID() == feed.getFeedID())
- reloadFavIcon.begin();
- });
icon_box.pack_start(m_icon, true, true, 0);
icon_box.pack_end(m_unread_eventbox, false, false, 10);
@@ -260,23 +255,20 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow {
return false;
}
- public async void reloadFavIcon(Gtk.Image? inIcon = null)
- {
- Feed feed = DataBase.readOnly().read_feed(m_article.getFeedID());
- var icon = yield FavIconManager.get_default().getIcon(feed);
- if(icon != null)
- {
- if(inIcon == null)
- m_icon.pixbuf = icon;
- else
- inIcon.pixbuf = icon;
- }
- }
-
private Gtk.Image createFavIcon()
{
var icon = new Gtk.Image.from_icon_name("feed-rss-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
- reloadFavIcon.begin(icon);
+
+ var manager = FavIconManager.get_default();
+ Feed feed = DataBase.readOnly().read_feed(m_article.getFeedID());
+ manager.getIcon.begin(feed, (obj, res) => {
+ var pixbuf = manager.getIcon.end(res);
+ if(pixbuf != null)
+ {
+ icon.pixbuf = pixbuf;
+ }
+ });
+
return icon;
}
diff --git a/src/Widgets/ColumnView.vala b/src/Widgets/ColumnView.vala
index 7f7078fb..0ae9aea3 100644
--- a/src/Widgets/ColumnView.vala
+++ b/src/Widgets/ColumnView.vala
@@ -517,10 +517,4 @@ public class FeedReader.ColumnView : Gtk.Paned {
m_article_view.clearContent();
m_feedList.clear();
}
-
- public void reloadFavIcons()
- {
- m_articleList.reloadFavIcons();
- m_feedList.reloadFavIcons();
- }
}
diff --git a/src/Widgets/FeedList.vala b/src/Widgets/FeedList.vala
index fa6ce6e7..ddaadc8c 100644
--- a/src/Widgets/FeedList.vala
+++ b/src/Widgets/FeedList.vala
@@ -1186,17 +1186,4 @@ public class FeedReader.feedList : Gtk.ScrolledWindow {
return false;
}
-
- public void reloadFavIcons()
- {
- var FeedChildList = m_list.get_children();
-
- foreach(Gtk.Widget row in FeedChildList)
- {
- var tmpRow = row as FeedRow;
- if(tmpRow != null)
- tmpRow.reloadFavIcon.begin();
- }
- }
-
}
diff --git a/src/Widgets/FeedRow.vala b/src/Widgets/FeedRow.vala
index f8dc4890..540d77fd 100644
--- a/src/Widgets/FeedRow.vala
+++ b/src/Widgets/FeedRow.vala
@@ -44,10 +44,6 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow {
var rowhight = 30;
m_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
m_icon = createFavIcon();
- FavIconManager.get_default().ReloadFavIcon.connect(feed => {
- if(m_feed.getFeedID() == feed.getFeedID())
- reloadFavIcon.begin();
- });
m_icon.margin_start = level * 24;
@@ -151,30 +147,20 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow {
}
}
- public async void reloadFavIcon(Gtk.Image? inIcon = null)
- {
- var icon = yield FavIconManager.get_default().getIcon(m_feed);
- if(icon == null)
- return;
-
- if(inIcon == null)
- {
- m_icon.pixbuf = icon;
- m_icon.get_style_context().remove_class("fr-sidebar-symbolic");
- }
- else
- {
- inIcon.pixbuf = icon;
- inIcon.get_style_context().remove_class("fr-sidebar-symbolic");
- }
- }
-
public Gtk.Image createFavIcon()
{
var icon = new Gtk.Image.from_icon_name("feed-rss-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
icon.get_style_context().add_class("fr-sidebar-symbolic");
- reloadFavIcon.begin(icon);
+ var manager = FavIconManager.get_default();
+ manager.getIcon.begin(m_feed, (obj, res) => {
+ var pixbuf = manager.getIcon.end(res);
+ if(pixbuf != null)
+ {
+ icon.pixbuf = pixbuf;
+ icon.get_style_context().remove_class("fr-sidebar-symbolic");
+ }
+ });
return icon;
}