Merge pull request #1359 from Wevah/favicon-fix

Fix favicons not loading for certain feeds
This commit is contained in:
Maurice Parker 2019-11-27 13:27:29 -06:00 committed by GitHub
commit 18045f2e2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 31 deletions

View File

@ -21,6 +21,8 @@ final class FaviconDownloader {
private let folder: String private let folder: String
private let diskCache: BinaryDiskCache private let diskCache: BinaryDiskCache
private var singleFaviconDownloaderCache = [String: SingleFaviconDownloader]() // faviconURL: SingleFaviconDownloader private var singleFaviconDownloaderCache = [String: SingleFaviconDownloader]() // faviconURL: SingleFaviconDownloader
private var remainingFaviconURLs = [String: ArraySlice<String>]() // homePageURL: array of faviconURLs that haven't been checked yet
private var homePageToFaviconURLCache = [String: String]() //homePageURL: faviconURL private var homePageToFaviconURLCache = [String: String]() //homePageURL: faviconURL
private var homePageURLsWithNoFaviconURL = Set<String>() private var homePageURLsWithNoFaviconURL = Set<String>()
private let queue: DispatchQueue private let queue: DispatchQueue
@ -49,11 +51,11 @@ final class FaviconDownloader {
assert(Thread.isMainThread) assert(Thread.isMainThread)
var homePageURL = feed.homePageURL
if let faviconURL = feed.faviconURL { if let faviconURL = feed.faviconURL {
return favicon(with: faviconURL) return favicon(with: faviconURL, homePageURL: homePageURL)
} }
var homePageURL = feed.homePageURL
if homePageURL == nil { if homePageURL == nil {
// Base homePageURL off feedURL if needed. Wont always be accurate, but is good enough. // Base homePageURL off feedURL if needed. Wont always be accurate, but is good enough.
if let feedURL = URL(string: feed.url), let scheme = feedURL.scheme, let host = feedURL.host { if let feedURL = URL(string: feed.url), let scheme = feedURL.scheme, let host = feedURL.host {
@ -83,9 +85,9 @@ final class FaviconDownloader {
return nil return nil
} }
func favicon(with faviconURL: String) -> RSImage? { func favicon(with faviconURL: String, homePageURL: String?) -> RSImage? {
let downloader = faviconDownloader(withURL: faviconURL) let downloader = faviconDownloader(withURL: faviconURL, homePageURL: homePageURL)
return downloader.image return downloader.image
} }
@ -95,17 +97,23 @@ final class FaviconDownloader {
if homePageURLsWithNoFaviconURL.contains(url) { if homePageURLsWithNoFaviconURL.contains(url) {
return nil return nil
} }
if let faviconURL = homePageToFaviconURLCache[url] { if let faviconURL = homePageToFaviconURLCache[url] {
return favicon(with: faviconURL) return favicon(with: faviconURL, homePageURL: url)
} }
findFaviconURL(with: url) { (faviconURL) in findFaviconURLs(with: url) { (faviconURLs) in
if let faviconURL = faviconURL { var hasIcons = false
self.homePageToFaviconURLCache[url] = faviconURL
let _ = self.favicon(with: faviconURL) if let faviconURLs = faviconURLs {
if let firstIconURL = faviconURLs.first {
hasIcons = true
let _ = self.favicon(with: firstIconURL, homePageURL: url)
self.remainingFaviconURLs[url] = faviconURLs.dropFirst()
}
} }
else {
if (!hasIcons) {
self.homePageURLsWithNoFaviconURL.insert(url) self.homePageURLsWithNoFaviconURL.insert(url)
} }
} }
@ -120,9 +128,28 @@ final class FaviconDownloader {
guard let singleFaviconDownloader = note.object as? SingleFaviconDownloader else { guard let singleFaviconDownloader = note.object as? SingleFaviconDownloader else {
return return
} }
guard let _ = singleFaviconDownloader.image else { guard let homePageURL = singleFaviconDownloader.homePageURL else {
return return
} }
guard let _ = singleFaviconDownloader.image else {
if let faviconURLs = remainingFaviconURLs[homePageURL] {
if let nextIconURL = faviconURLs.first {
let _ = favicon(with: nextIconURL, homePageURL: singleFaviconDownloader.homePageURL)
remainingFaviconURLs[homePageURL] = faviconURLs.dropFirst();
} else {
remainingFaviconURLs[homePageURL] = nil
}
}
return
}
remainingFaviconURLs[homePageURL] = nil
if let url = singleFaviconDownloader.homePageURL {
if self.homePageToFaviconURLCache[url] == nil {
self.homePageToFaviconURLCache[url] = singleFaviconDownloader.faviconURL
}
}
postFaviconDidBecomeAvailableNotification(singleFaviconDownloader.faviconURL) postFaviconDidBecomeAvailableNotification(singleFaviconDownloader.faviconURL)
} }
@ -132,38 +159,40 @@ private extension FaviconDownloader {
static let localeForLowercasing = Locale(identifier: "en_US") static let localeForLowercasing = Locale(identifier: "en_US")
func findFaviconURL(with homePageURL: String, _ completion: @escaping (String?) -> Void) { func findFaviconURLs(with homePageURL: String, _ completion: @escaping ([String]?) -> Void) {
guard let url = URL(string: homePageURL) else { guard let url = URL(string: homePageURL) else {
completion(nil) completion(nil)
return return
} }
FaviconURLFinder.findFaviconURL(homePageURL) { (faviconURL) in FaviconURLFinder.findFaviconURLs(homePageURL) { (faviconURLs) in
var defaultFaviconURL: String? = nil
if let faviconURL = faviconURL { if let scheme = url.scheme, let host = url.host {
completion(faviconURL) defaultFaviconURL = "\(scheme)://\(host)/favicon.ico".lowercased(with: FaviconDownloader.localeForLowercasing)
}
if var faviconURLs = faviconURLs {
if let defaultFaviconURL = defaultFaviconURL {
faviconURLs.append(defaultFaviconURL)
}
completion(faviconURLs)
return return
} }
guard let scheme = url.scheme, let host = url.host else { completion(defaultFaviconURL != nil ? [defaultFaviconURL!] : nil)
completion(nil)
return
}
let defaultFaviconURL = "\(scheme)://\(host)/favicon.ico".lowercased(with: FaviconDownloader.localeForLowercasing)
completion(defaultFaviconURL)
} }
} }
func faviconDownloader(withURL faviconURL: String) -> SingleFaviconDownloader { func faviconDownloader(withURL faviconURL: String, homePageURL: String?) -> SingleFaviconDownloader {
if let downloader = singleFaviconDownloaderCache[faviconURL] { if let downloader = singleFaviconDownloaderCache[faviconURL] {
downloader.downloadFaviconIfNeeded() downloader.downloadFaviconIfNeeded()
return downloader return downloader
} }
let downloader = SingleFaviconDownloader(faviconURL: faviconURL, diskCache: diskCache, queue: queue) let downloader = SingleFaviconDownloader(faviconURL: faviconURL, homePageURL: homePageURL, diskCache: diskCache, queue: queue)
singleFaviconDownloaderCache[faviconURL] = downloader singleFaviconDownloaderCache[faviconURL] = downloader
return downloader return downloader
} }

View File

@ -9,11 +9,11 @@
import Foundation import Foundation
import RSParser import RSParser
// The favicon URL may be specified in the head section of the home page. // The favicon URLs may be specified in the head section of the home page.
struct FaviconURLFinder { struct FaviconURLFinder {
static func findFaviconURL(_ homePageURL: String, _ callback: @escaping (String?) -> Void) { static func findFaviconURLs(_ homePageURL: String, _ callback: @escaping ([String]?) -> Void) {
guard let _ = URL(string: homePageURL) else { guard let _ = URL(string: homePageURL) else {
callback(nil) callback(nil)
@ -21,7 +21,7 @@ struct FaviconURLFinder {
} }
HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { (htmlMetadata) in HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { (htmlMetadata) in
callback(htmlMetadata?.faviconLink) callback(htmlMetadata?.faviconLinks)
} }
} }
} }

View File

@ -26,6 +26,7 @@ final class SingleFaviconDownloader {
let faviconURL: String let faviconURL: String
var image: RSImage? var image: RSImage?
let homePageURL: String?
private var lastDownloadAttemptDate: Date private var lastDownloadAttemptDate: Date
private var diskStatus = DiskStatus.unknown private var diskStatus = DiskStatus.unknown
@ -36,9 +37,10 @@ final class SingleFaviconDownloader {
return (faviconURL as NSString).rs_md5Hash() return (faviconURL as NSString).rs_md5Hash()
} }
init(faviconURL: String, diskCache: BinaryDiskCache, queue: DispatchQueue) { init(faviconURL: String, homePageURL: String?, diskCache: BinaryDiskCache, queue: DispatchQueue) {
self.faviconURL = faviconURL self.faviconURL = faviconURL
self.homePageURL = homePageURL
self.diskCache = diskCache self.diskCache = diskCache
self.queue = queue self.queue = queue
self.lastDownloadAttemptDate = Date() self.lastDownloadAttemptDate = Date()
@ -83,8 +85,9 @@ private extension SingleFaviconDownloader {
if let image = image { if let image = image {
self.image = image self.image = image
self.postDidLoadFaviconNotification()
} }
self.postDidLoadFaviconNotification()
} }
} }
} }

@ -1 +1 @@
Subproject commit f01129d762eba20cd11a680bbde651ca75639ef3 Subproject commit 81c400a7665309a08414bf43ca5161d90d072501