Add ImageDownloader.
This commit is contained in:
parent
e026e159d1
commit
33fef5ea1c
|
@ -13,6 +13,7 @@
|
|||
842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */; };
|
||||
842E45E71ED8C747000A8B52 /* DB5.plist in Resources */ = {isa = PBXBuildFile; fileRef = 842E45E61ED8C747000A8B52 /* DB5.plist */; };
|
||||
84513F901FAA63950023A1A9 /* FeedListControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */; };
|
||||
845213231FCA5B11003B6E93 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845213221FCA5B10003B6E93 /* ImageDownloader.swift */; };
|
||||
845A29091FC74B8E007B49E3 /* SingleFaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29081FC74B8E007B49E3 /* SingleFaviconDownloader.swift */; };
|
||||
845A291B1FC75AA6007B49E3 /* SeekingFavicon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A291A1FC75AA6007B49E3 /* SeekingFavicon.swift */; };
|
||||
845A29221FC9251E007B49E3 /* SidebarCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29211FC9251E007B49E3 /* SidebarCellLayout.swift */; };
|
||||
|
@ -407,6 +408,7 @@
|
|||
842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowSplitView.swift; sourceTree = "<group>"; };
|
||||
842E45E61ED8C747000A8B52 /* DB5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = DB5.plist; path = Evergreen/Resources/DB5.plist; sourceTree = "<group>"; };
|
||||
84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListControlsView.swift; sourceTree = "<group>"; };
|
||||
845213221FCA5B10003B6E93 /* ImageDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDownloader.swift; sourceTree = "<group>"; };
|
||||
845A29081FC74B8E007B49E3 /* SingleFaviconDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleFaviconDownloader.swift; sourceTree = "<group>"; };
|
||||
845A291A1FC75AA6007B49E3 /* SeekingFavicon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeekingFavicon.swift; sourceTree = "<group>"; };
|
||||
845A29211FC9251E007B49E3 /* SidebarCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarCellLayout.swift; sourceTree = "<group>"; };
|
||||
|
@ -558,6 +560,15 @@
|
|||
path = Evergreen/MainWindow;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
845213211FCA5B10003B6E93 /* Images */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
845213221FCA5B10003B6E93 /* ImageDownloader.swift */,
|
||||
);
|
||||
name = Images;
|
||||
path = Evergreen/Images;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
845A29251FC928C7007B49E3 /* Cell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -785,6 +796,7 @@
|
|||
84F2D5341FC22FCB00998D64 /* PseudoFeeds */,
|
||||
849A97561ED9EB0D007D329B /* Data */,
|
||||
848F6AE31FC29CFA002D422E /* Favicons */,
|
||||
845213211FCA5B10003B6E93 /* Images */,
|
||||
849A97961ED9EFAA007D329B /* Extensions */,
|
||||
849A97991ED9EFB6007D329B /* Resources */,
|
||||
84FB9A2C1EDCD6A4003D53B9 /* Frameworks */,
|
||||
|
@ -1351,6 +1363,7 @@
|
|||
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
|
||||
849A97831ED9EC63007D329B /* StatusBarView.swift in Sources */,
|
||||
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */,
|
||||
845213231FCA5B11003B6E93 /* ImageDownloader.swift in Sources */,
|
||||
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
|
||||
849A97921ED9EF65007D329B /* IndeterminateProgressWindowController.swift in Sources */,
|
||||
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
|
||||
|
|
|
@ -22,6 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
|
||||
var currentTheme: VSTheme!
|
||||
var faviconDownloader: FaviconDownloader!
|
||||
var imageDownloader: ImageDownloader!
|
||||
var appName: String!
|
||||
var pseudoFeeds = [PseudoFeed]()
|
||||
|
||||
|
@ -130,11 +131,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
|
||||
let tempDirectory = NSTemporaryDirectory()
|
||||
let cacheFolder = (tempDirectory as NSString).appendingPathComponent("com.ranchero.evergreen")
|
||||
|
||||
let faviconsFolder = (cacheFolder as NSString).appendingPathComponent("Favicons")
|
||||
let faviconsFolderURL = URL(fileURLWithPath: faviconsFolder)
|
||||
try! FileManager.default.createDirectory(at: faviconsFolderURL, withIntermediateDirectories: true, attributes: nil)
|
||||
faviconDownloader = FaviconDownloader(folder: faviconsFolder)
|
||||
|
||||
let imagesFolder = (cacheFolder as NSString).appendingPathComponent("Images")
|
||||
let imagesFolderURL = URL(fileURLWithPath: imagesFolder)
|
||||
try! FileManager.default.createDirectory(at: imagesFolderURL, withIntermediateDirectories: true, attributes: nil)
|
||||
imageDownloader = ImageDownloader(folder: imagesFolder)
|
||||
|
||||
let todayFeed = SmartFeed(delegate: TodayFeedDelegate())
|
||||
let unreadFeed = UnreadFeed()
|
||||
let starredFeed = SmartFeed(delegate: StarredFeedDelegate())
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// ImageDownloader.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 11/25/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import RSCore
|
||||
import RSWeb
|
||||
|
||||
extension Notification.Name {
|
||||
|
||||
static let ImageDidBecomeAvailable = Notification.Name("ImageDidBecomeAvailableNotification") // ImageDownloader.UserInfoKey.imageURL
|
||||
}
|
||||
|
||||
final class ImageDownloader {
|
||||
|
||||
private let folder: String
|
||||
private var diskCache: BinaryDiskCache
|
||||
private let queue: DispatchQueue
|
||||
private var imageCache = [String: NSImage]() // url: image
|
||||
|
||||
struct UserInfoKey {
|
||||
static let imageURL = "imageURL"
|
||||
}
|
||||
|
||||
init(folder: String) {
|
||||
|
||||
self.folder = folder
|
||||
self.diskCache = BinaryDiskCache(folder: folder)
|
||||
self.queue = DispatchQueue(label: "ImageDownloader serial queue - \(folder)")
|
||||
}
|
||||
|
||||
func image(for url: String) -> NSImage? {
|
||||
|
||||
if let image = imageCache[url] {
|
||||
return image
|
||||
}
|
||||
|
||||
findImage(url)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private extension ImageDownloader {
|
||||
|
||||
func cacheImage(_ url: String, _ image: NSImage) {
|
||||
|
||||
imageCache[url] = image
|
||||
postImageDidBecomeAvailableNotification(url)
|
||||
}
|
||||
|
||||
func findImage(_ url: String) {
|
||||
|
||||
readFromDisk(url) { (image) in
|
||||
|
||||
if let image = image {
|
||||
self.cacheImage(url, image)
|
||||
return
|
||||
}
|
||||
|
||||
self.downloadImage(url) { (image) in
|
||||
|
||||
if let image = image {
|
||||
self.cacheImage(url, image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readFromDisk(_ url: String, _ callback: @escaping (NSImage?) -> Void) {
|
||||
|
||||
queue.async {
|
||||
|
||||
if let data = self.diskCache[self.diskKey(url)], !data.isEmpty {
|
||||
NSImage.rs_image(with: data, imageResultBlock: callback)
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
callback(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func downloadImage(_ url: String, _ callback: @escaping (NSImage?) -> Void) {
|
||||
|
||||
guard let url = URL(string: url) else {
|
||||
callback(nil)
|
||||
return
|
||||
}
|
||||
|
||||
downloadUsingCache(url) { (data, response, error) in
|
||||
|
||||
if let data = data, !data.isEmpty, let response = response, response.statusIsOK, error == nil {
|
||||
self.saveToDisk(url.absoluteString, data)
|
||||
NSImage.rs_image(with: data, imageResultBlock: callback)
|
||||
return
|
||||
}
|
||||
|
||||
if let error = error {
|
||||
appDelegate.logMessage("Error downloading image at \(url): \(error)", type: .warning)
|
||||
}
|
||||
|
||||
callback(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func saveToDisk(_ url: String, _ data: Data) {
|
||||
|
||||
queue.async {
|
||||
self.diskCache[self.diskKey(url)] = data
|
||||
}
|
||||
}
|
||||
|
||||
func diskKey(_ url: String) -> String {
|
||||
|
||||
return (url as NSString).rs_md5Hash()
|
||||
}
|
||||
|
||||
func postImageDidBecomeAvailableNotification(_ url: String) {
|
||||
|
||||
NotificationCenter.default.post(name: .ImageDidBecomeAvailable, object: self, userInfo: [UserInfoKey.imageURL: url])
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue