Make progress on FaviconDownloader.
This commit is contained in:
parent
86907f6aab
commit
6979b85fb5
@ -21,7 +21,7 @@
|
||||
846E77411F6EF6A100A165E2 /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; };
|
||||
846E77421F6EF6A100A165E2 /* Database.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */; };
|
||||
848F6AE51FC29CFB002D422E /* FaviconCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconCache.swift */; };
|
||||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; };
|
||||
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */; };
|
||||
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97511ED9EAC0007D329B /* AddFeedController.swift */; };
|
||||
849A97541ED9EAC0007D329B /* AddFeedWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97521ED9EAC0007D329B /* AddFeedWindowController.swift */; };
|
||||
@ -409,7 +409,7 @@
|
||||
846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = "<group>"; };
|
||||
846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = "<group>"; };
|
||||
84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkReadOrUnreadCommand.swift; sourceTree = "<group>"; };
|
||||
848F6AE41FC29CFA002D422E /* FaviconCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconCache.swift; sourceTree = "<group>"; };
|
||||
848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = "<group>"; };
|
||||
849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = "<group>"; };
|
||||
849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = "<group>"; };
|
||||
849A97521ED9EAC0007D329B /* AddFeedWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedWindowController.swift; path = AddFeed/AddFeedWindowController.swift; sourceTree = "<group>"; };
|
||||
@ -578,7 +578,7 @@
|
||||
848F6AE31FC29CFA002D422E /* Favicons */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
848F6AE41FC29CFA002D422E /* FaviconCache.swift */,
|
||||
848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */,
|
||||
);
|
||||
name = Favicons;
|
||||
path = Evergreen/Favicons;
|
||||
@ -1319,7 +1319,7 @@
|
||||
849A97791ED9EC04007D329B /* TimelineStringUtilities.swift in Sources */,
|
||||
84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */,
|
||||
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */,
|
||||
848F6AE51FC29CFB002D422E /* FaviconCache.swift in Sources */,
|
||||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */,
|
||||
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
|
||||
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
|
||||
849A97831ED9EC63007D329B /* StatusBarView.swift in Sources */,
|
||||
|
@ -1,25 +0,0 @@
|
||||
//
|
||||
// FaviconCache.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 11/19/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Data
|
||||
|
||||
extension Notification.Name {
|
||||
|
||||
static let FaviconDidDownload = Notification.Name("FaviconDidDownloadNotification")
|
||||
}
|
||||
|
||||
final class FaviconCache {
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func favicon(for feed: Feed) -> NSImage? {
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
145
Evergreen/Favicons/FaviconDownloader.swift
Normal file
145
Evergreen/Favicons/FaviconDownloader.swift
Normal file
@ -0,0 +1,145 @@
|
||||
//
|
||||
// FaviconDownloader.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 11/19/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Data
|
||||
import RSCore
|
||||
import RSWeb
|
||||
|
||||
extension Notification.Name {
|
||||
|
||||
static let FaviconDidBecomeAvailable = Notification.Name("FaviconDidBecomeAvailableNotification") // userInfo keys: homePageURL, faviconURL, image
|
||||
}
|
||||
|
||||
final class FaviconDownloader {
|
||||
|
||||
private var cache = ThreadSafeCache<NSImage>() // faviconURL: NSImage
|
||||
private var faviconURLCache = ThreadSafeCache<String>() // homePageURL: faviconURL
|
||||
private let folder: String
|
||||
private var urlsBeingDownloaded = Set<String>()
|
||||
private let binaryCache: RSBinaryCache
|
||||
private var badImages = Set<String>() // keys for images on disk that NSImage can’t handle
|
||||
private let queue: DispatchQueue
|
||||
|
||||
public struct UserInfoKey {
|
||||
static let homePageURL = "homePageURL"
|
||||
static let faviconURL = "faviconURL"
|
||||
static let image = "image" // NSImage
|
||||
}
|
||||
|
||||
init(folder: String) {
|
||||
|
||||
self.folder = folder
|
||||
self.binaryCache = RSBinaryCache(folder: folder)
|
||||
self.queue = DispatchQueue(label: "FaviconCache serial queue - \(folder)")
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func favicon(for feed: Feed) -> NSImage? {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
if let faviconURL = faviconURL(for: feed) {
|
||||
|
||||
if let cachedFavicon = cache[faviconURL] {
|
||||
return cachedFavicon
|
||||
}
|
||||
if shouldDownloadFaviconURL(faviconURL) {
|
||||
downloadFavicon(faviconURL)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private extension FaviconDownloader {
|
||||
|
||||
func shouldDownloadFaviconURL(_ faviconURL: String) -> Bool {
|
||||
|
||||
return !urlsBeingDownloaded.contains(faviconURL)
|
||||
}
|
||||
|
||||
func downloadFavicon(_ faviconURL: String) {
|
||||
|
||||
guard let url = URL(string: faviconURL) else {
|
||||
return
|
||||
}
|
||||
|
||||
urlsBeingDownloaded.insert(faviconURL)
|
||||
|
||||
download(url) { (data, response, error) in
|
||||
|
||||
self.urlsBeingDownloaded.remove(faviconURL)
|
||||
if let data = data {
|
||||
self.queue.async {
|
||||
let _ = NSImage(data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func faviconURL(for feed: Feed) -> String? {
|
||||
|
||||
if let faviconURL = feed.faviconURL {
|
||||
return faviconURL
|
||||
}
|
||||
|
||||
if let homePageURL = feed.homePageURL {
|
||||
return faviconURLCache[homePageURL]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readFaviconFromDisk(_ faviconURL: String, _ callback: @escaping (NSImage?) -> Void) {
|
||||
|
||||
queue.async {
|
||||
let image = self.tryToInstantiateNSImageFromDisk(faviconURL)
|
||||
DispatchQueue.main.async {
|
||||
callback(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tryToInstantiateNSImageFromDisk(_ faviconURL: String) -> NSImage? {
|
||||
|
||||
// Call on serial queue.
|
||||
|
||||
if badImages.contains(faviconURL) {
|
||||
return nil
|
||||
}
|
||||
|
||||
let key = keyFor(faviconURL)
|
||||
var data: Data?
|
||||
|
||||
do {
|
||||
data = try binaryCache.binaryData(forKey: key)
|
||||
}
|
||||
catch {
|
||||
return nil
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let image = NSImage(data: data!) else {
|
||||
badImages.insert(faviconURL)
|
||||
return nil
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
func keyFor(_ faviconURL: String) -> String {
|
||||
|
||||
return (faviconURL as NSString).rs_md5Hash()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user