2022-11-10 17:11:28 +00:00
|
|
|
import Foundation
|
|
|
|
import Logging
|
|
|
|
|
|
|
|
struct URLBookmarkModel {
|
2022-11-14 22:13:29 +00:00
|
|
|
static let bookmarkPrefix = "urlbookmark-"
|
2022-11-10 17:11:28 +00:00
|
|
|
static var shared = URLBookmarkModel()
|
2022-11-14 22:13:29 +00:00
|
|
|
|
2022-11-10 17:11:28 +00:00
|
|
|
var logger = Logger(label: "stream.yattee.url-bookmark")
|
|
|
|
|
2022-11-14 22:13:29 +00:00
|
|
|
var allBookmarksKeys: [String] {
|
|
|
|
guard let defaults = CacheModel.shared.bookmarksDefaults else { return [] }
|
|
|
|
|
|
|
|
return defaults.dictionaryRepresentation().keys.filter { $0.starts(with: Self.bookmarkPrefix) }
|
|
|
|
}
|
|
|
|
|
|
|
|
var allURLs: [URL] {
|
|
|
|
allBookmarksKeys.compactMap { urlFromBookmark($0) }
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:11:28 +00:00
|
|
|
func saveBookmark(_ url: URL) {
|
2022-12-04 12:21:50 +00:00
|
|
|
var urlForBookmark = url
|
|
|
|
if let yatteeSanitizedUrl = url.byReplacingYatteeProtocol() {
|
|
|
|
urlForBookmark = yatteeSanitizedUrl
|
|
|
|
}
|
|
|
|
|
|
|
|
guard urlForBookmark.isFileURL else {
|
|
|
|
logger.error("trying to save bookmark for something that is not a file")
|
|
|
|
logger.error("not a file: \(urlForBookmark.absoluteString)")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-11 11:27:37 +00:00
|
|
|
guard let defaults = CacheModel.shared.bookmarksDefaults else {
|
|
|
|
logger.error("could not open bookmarks defaults")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-12-04 12:21:50 +00:00
|
|
|
if let bookmarkData = try? urlForBookmark.bookmarkData(options: bookmarkCreationOptions, includingResourceValuesForKeys: nil, relativeTo: nil) {
|
|
|
|
defaults.set(bookmarkData, forKey: bookmarkKey(urlForBookmark))
|
|
|
|
logger.info("saved bookmark for \(bookmarkKey(urlForBookmark))")
|
|
|
|
} else {
|
|
|
|
logger.error("no bookmark data for \(urlForBookmark)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func saveBookmark(_ url: NSURL) {
|
2022-11-25 18:36:23 +00:00
|
|
|
guard url.isFileURL else {
|
|
|
|
logger.error("trying to save bookmark for something that is not a file")
|
2022-12-04 12:21:50 +00:00
|
|
|
logger.error("not a file: \(url.absoluteString)")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let defaults = CacheModel.shared.bookmarksDefaults else {
|
|
|
|
logger.error("could not open bookmarks defaults")
|
2022-11-25 18:36:23 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-10 17:11:28 +00:00
|
|
|
if let bookmarkData = try? url.bookmarkData(options: bookmarkCreationOptions, includingResourceValuesForKeys: nil, relativeTo: nil) {
|
2022-11-14 22:13:29 +00:00
|
|
|
defaults.set(bookmarkData, forKey: bookmarkKey(url))
|
|
|
|
logger.info("saved bookmark for \(bookmarkKey(url))")
|
2022-11-11 11:27:37 +00:00
|
|
|
} else {
|
2022-11-14 22:13:29 +00:00
|
|
|
logger.error("no bookmark data for \(bookmarkKey(url))")
|
2022-11-10 17:11:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadBookmark(_ url: URL) -> URL? {
|
2022-12-04 12:21:50 +00:00
|
|
|
var urlForBookmark = url
|
|
|
|
if let yatteeSanitizedUrl = url.byReplacingYatteeProtocol() {
|
|
|
|
urlForBookmark = yatteeSanitizedUrl
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.info("loading bookmark for \(bookmarkKey(urlForBookmark))")
|
2022-11-10 17:11:28 +00:00
|
|
|
|
2022-11-11 11:27:37 +00:00
|
|
|
guard let defaults = CacheModel.shared.bookmarksDefaults else {
|
|
|
|
logger.error("could not open bookmarks defaults")
|
2022-11-10 17:11:28 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-04 12:21:50 +00:00
|
|
|
if let data = defaults.data(forKey: bookmarkKey(urlForBookmark)) {
|
2022-11-11 11:27:37 +00:00
|
|
|
do {
|
|
|
|
var isStale = false
|
|
|
|
let url = try URL(
|
|
|
|
resolvingBookmarkData: data,
|
|
|
|
options: bookmarkResolutionOptions,
|
|
|
|
relativeTo: nil,
|
|
|
|
bookmarkDataIsStale: &isStale
|
|
|
|
)
|
|
|
|
if isStale {
|
2022-12-04 12:21:50 +00:00
|
|
|
saveBookmark(urlForBookmark)
|
2022-11-11 11:27:37 +00:00
|
|
|
}
|
2022-12-04 12:21:50 +00:00
|
|
|
logger.info("loaded bookmark for \(bookmarkKey(urlForBookmark))")
|
2022-11-11 11:27:37 +00:00
|
|
|
|
|
|
|
return url
|
|
|
|
} catch {
|
|
|
|
print("Error resolving bookmark:", error)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else {
|
2022-12-04 12:21:50 +00:00
|
|
|
logger.warning("could not find bookmark for \(bookmarkKey(urlForBookmark))")
|
2022-11-10 17:11:28 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-12 23:01:04 +00:00
|
|
|
func removeBookmark(_ url: URL) {
|
2022-11-14 22:13:29 +00:00
|
|
|
logger.info("removing bookmark for \(bookmarkKey(url))")
|
2022-11-12 23:01:04 +00:00
|
|
|
|
|
|
|
guard let defaults = CacheModel.shared.bookmarksDefaults else {
|
|
|
|
logger.error("could not open bookmarks defaults")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-14 22:13:29 +00:00
|
|
|
defaults.removeObject(forKey: bookmarkKey(url))
|
|
|
|
}
|
|
|
|
|
|
|
|
func refreshAll() {
|
2022-11-17 21:49:08 +00:00
|
|
|
logger.info("refreshing all bookmarks")
|
2022-11-14 22:13:29 +00:00
|
|
|
|
|
|
|
allURLs.forEach { url in
|
|
|
|
if loadBookmark(url) != nil {
|
|
|
|
logger.info("bookmark for \(url) exists")
|
|
|
|
} else {
|
|
|
|
logger.info("bookmark does not exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func bookmarkKey(_ url: URL) -> String {
|
2022-12-04 12:21:50 +00:00
|
|
|
"\(Self.bookmarkPrefix)\(NSString(string: url.absoluteString).standardizingPath)"
|
|
|
|
}
|
|
|
|
|
|
|
|
private func bookmarkKey(_ url: NSURL) -> String {
|
|
|
|
"\(Self.bookmarkPrefix)\(url.standardizingPath?.absoluteString ?? "unknown")"
|
2022-11-14 22:13:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private func urlFromBookmark(_ key: String) -> URL? {
|
|
|
|
if let urlString = key.components(separatedBy: Self.bookmarkPrefix).last {
|
|
|
|
return URL(string: urlString)
|
|
|
|
}
|
|
|
|
return nil
|
2022-11-12 23:01:04 +00:00
|
|
|
}
|
|
|
|
|
2022-11-14 22:13:29 +00:00
|
|
|
private var bookmarkCreationOptions: URL.BookmarkCreationOptions {
|
2022-11-10 17:11:28 +00:00
|
|
|
#if os(macOS)
|
|
|
|
return [.withSecurityScope, .securityScopeAllowOnlyReadAccess]
|
|
|
|
#else
|
|
|
|
return []
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-11-14 22:13:29 +00:00
|
|
|
private var bookmarkResolutionOptions: URL.BookmarkResolutionOptions {
|
2022-11-10 17:11:28 +00:00
|
|
|
#if os(macOS)
|
|
|
|
return [.withSecurityScope]
|
|
|
|
#else
|
|
|
|
return []
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|