mirror of
https://github.com/yattee/yattee.git
synced 2025-08-09 20:24:06 +00:00
Channels caching
This commit is contained in:
@@ -519,6 +519,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
return Channel(
|
||||
app: .invidious,
|
||||
id: json["authorId"].stringValue,
|
||||
name: json["author"].stringValue,
|
||||
bannerURL: json["authorBanners"].arrayValue.first?.dictionaryValue["url"]?.url,
|
||||
@@ -666,7 +667,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
likeCount: details["likeCount"]?.int ?? 0,
|
||||
text: details["content"]?.string ?? "",
|
||||
repliesPage: details["replies"]?.dictionaryValue["continuation"]?.string,
|
||||
channel: Channel(id: channelId, name: author)
|
||||
channel: Channel(app: .invidious, id: channelId, name: author)
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -472,6 +472,7 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
|
||||
|
||||
func extractChannel(from json: JSON) -> Channel {
|
||||
Channel(
|
||||
app: .peerTube,
|
||||
id: json["id"].stringValue,
|
||||
name: json["name"].stringValue
|
||||
)
|
||||
@@ -572,7 +573,7 @@ final class PeerTubeAPI: Service, ObservableObject, VideosAPI {
|
||||
likeCount: details["likeCount"]?.int ?? 0,
|
||||
text: details["content"]?.string ?? "",
|
||||
repliesPage: details["replies"]?.dictionaryValue["continuation"]?.string,
|
||||
channel: Channel(id: channelId, name: author)
|
||||
channel: Channel(app: .peerTube, id: channelId, name: author)
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -410,6 +410,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
} ?? [Channel.Tab]()
|
||||
|
||||
return Channel(
|
||||
app: .piped,
|
||||
id: id,
|
||||
name: name,
|
||||
bannerURL: attributes["bannerUrl"]?.url,
|
||||
@@ -488,7 +489,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
published: published ?? "",
|
||||
views: details["views"]?.int ?? 0,
|
||||
description: description,
|
||||
channel: Channel(id: channelId, name: author, thumbnailURL: authorThumbnailURL, subscriptionsCount: subscriptionsCount),
|
||||
channel: Channel(app: .piped, id: channelId, name: author, thumbnailURL: authorThumbnailURL, subscriptionsCount: subscriptionsCount),
|
||||
thumbnails: thumbnails,
|
||||
live: live,
|
||||
likes: details["likes"]?.int,
|
||||
@@ -667,7 +668,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
likeCount: details["likeCount"]?.int ?? 0,
|
||||
text: extractCommentText(from: details["commentText"]?.stringValue),
|
||||
repliesPage: details["repliesPage"]?.string,
|
||||
channel: Channel(id: channelId, name: author)
|
||||
channel: Channel(app: .piped, id: channelId, name: author)
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ struct BaseCacheModel {
|
||||
[
|
||||
FeedCacheModel.shared,
|
||||
VideosCacheModel.shared,
|
||||
ChannelsCacheModel.shared,
|
||||
PlaylistsCacheModel.shared,
|
||||
ChannelPlaylistsCacheModel.shared,
|
||||
SubscribedChannelsModel.shared
|
||||
|
46
Model/Cache/ChannelsCacheModel.swift
Normal file
46
Model/Cache/ChannelsCacheModel.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
import Cache
|
||||
import Foundation
|
||||
import Logging
|
||||
import SwiftyJSON
|
||||
|
||||
struct ChannelsCacheModel: CacheModel {
|
||||
static let shared = ChannelsCacheModel()
|
||||
let logger = Logger(label: "stream.yattee.cache.channels")
|
||||
|
||||
static let diskConfig = DiskConfig(name: "channels")
|
||||
static let memoryConfig = MemoryConfig()
|
||||
|
||||
let storage = try? Storage<String, JSON>(
|
||||
diskConfig: Self.diskConfig,
|
||||
memoryConfig: Self.memoryConfig,
|
||||
transformer: BaseCacheModel.jsonTransformer
|
||||
)
|
||||
|
||||
func store(_ channel: Channel) {
|
||||
guard channel.hasExtendedDetails else {
|
||||
logger.warning("not caching \(channel.cacheKey)")
|
||||
return
|
||||
}
|
||||
|
||||
logger.info("caching \(channel.cacheKey)")
|
||||
try? storage?.setObject(channel.json, forKey: channel.cacheKey)
|
||||
}
|
||||
|
||||
func storeIfMissing(_ channel: Channel) {
|
||||
guard let storage, !storage.objectExists(forKey: channel.cacheKey) else {
|
||||
return
|
||||
}
|
||||
|
||||
store(channel)
|
||||
}
|
||||
|
||||
func retrieve(_ cacheKey: String) -> Channel? {
|
||||
logger.info("retrieving cache for \(cacheKey)")
|
||||
|
||||
if let json = try? storage?.object(forKey: cacheKey) {
|
||||
return Channel.from(json)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@@ -69,6 +69,7 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
|
||||
.onSuccess { resource in
|
||||
if let channels: [Channel] = resource.typedContent() {
|
||||
self.channels = channels
|
||||
channels.forEach { ChannelsCacheModel.shared.storeIfMissing($0) }
|
||||
self.storeChannels(account: account, channels: channels)
|
||||
FeedModel.shared.calculateUnwatchedFeed()
|
||||
onSuccess()
|
||||
@@ -93,6 +94,8 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
|
||||
let date = iso8601DateFormatter.string(from: Date())
|
||||
logger.info("caching channels \(channelsDateCacheKey(account)) -- \(date)")
|
||||
|
||||
channels.forEach { ChannelsCacheModel.shared.storeIfMissing($0) }
|
||||
|
||||
let dateObject: JSON = ["date": date]
|
||||
let channelsObject: JSON = ["channels": channels.map(\.json).map(\.object)]
|
||||
|
||||
@@ -106,7 +109,16 @@ final class SubscribedChannelsModel: ObservableObject, CacheModel {
|
||||
if let json = try? storage?.object(forKey: channelsCacheKey(account)),
|
||||
let channels = json.dictionaryValue["channels"]
|
||||
{
|
||||
return channels.arrayValue.map { Channel.from($0) }
|
||||
return channels.arrayValue.map { json in
|
||||
let channel = Channel.from(json)
|
||||
if !channel.hasExtendedDetails,
|
||||
let cache = ChannelsCacheModel.shared.retrieve(channel.cacheKey)
|
||||
{
|
||||
return cache
|
||||
}
|
||||
|
||||
return channel
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
|
@@ -19,6 +19,8 @@ struct VideosCacheModel: CacheModel {
|
||||
func storeVideo(_ video: Video) {
|
||||
logger.info("caching \(video.cacheKey)")
|
||||
try? storage?.setObject(video.json, forKey: video.cacheKey)
|
||||
|
||||
ChannelsCacheModel.shared.storeIfMissing(video.channel)
|
||||
}
|
||||
|
||||
func retrieveVideo(_ cacheKey: String) -> Video? {
|
||||
|
@@ -64,6 +64,10 @@ struct Channel: Identifiable, Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
var app: VideosApp
|
||||
var instanceID: Instance.ID?
|
||||
var instanceURL: URL?
|
||||
|
||||
var id: String
|
||||
var name: String
|
||||
var bannerURL: URL?
|
||||
@@ -112,14 +116,37 @@ struct Channel: Identifiable, Hashable {
|
||||
|
||||
var json: JSON {
|
||||
[
|
||||
"app": app.rawValue,
|
||||
"id": id,
|
||||
"name": name,
|
||||
"thumbnailURL": thumbnailURL?.absoluteString ?? ""
|
||||
]
|
||||
}
|
||||
|
||||
var cacheKey: String {
|
||||
switch app {
|
||||
case .local:
|
||||
return id
|
||||
case .invidious:
|
||||
return "youtube-\(id)"
|
||||
case .piped:
|
||||
return "youtube-\(id)"
|
||||
case .peerTube:
|
||||
return "peertube-\(instanceURL?.absoluteString ?? "unknown-instance")-\(id)"
|
||||
}
|
||||
}
|
||||
|
||||
var hasExtendedDetails: Bool {
|
||||
thumbnailURL != nil
|
||||
}
|
||||
|
||||
var thumbnailURLOrCached: URL? {
|
||||
thumbnailURL ?? ChannelsCacheModel.shared.retrieve(cacheKey)?.thumbnailURL
|
||||
}
|
||||
|
||||
static func from(_ json: JSON) -> Self {
|
||||
.init(
|
||||
app: VideosApp(rawValue: json["app"].stringValue) ?? .local,
|
||||
id: json["id"].stringValue,
|
||||
name: json["name"].stringValue,
|
||||
thumbnailURL: json["thumbnailURL"].url
|
||||
|
@@ -98,7 +98,7 @@ struct RecentItem: Defaults.Serializable, Identifiable {
|
||||
return nil
|
||||
}
|
||||
|
||||
return Channel(id: id, name: title)
|
||||
return Channel(app: .invidious, id: id, name: title)
|
||||
}
|
||||
|
||||
var playlist: ChannelPlaylist? {
|
||||
|
@@ -69,7 +69,7 @@ struct Video: Identifiable, Equatable, Hashable {
|
||||
views: Int = 0,
|
||||
description: String? = nil,
|
||||
genre: String? = nil,
|
||||
channel: Channel = .init(id: "", name: ""),
|
||||
channel: Channel? = nil,
|
||||
thumbnails: [Thumbnail] = [],
|
||||
indexID: String? = nil,
|
||||
live: Bool = false,
|
||||
@@ -96,7 +96,7 @@ struct Video: Identifiable, Equatable, Hashable {
|
||||
self.views = views
|
||||
self.description = description
|
||||
self.genre = genre
|
||||
self.channel = channel
|
||||
self.channel = channel ?? .init(app: app, id: "", name: "")
|
||||
self.thumbnails = thumbnails
|
||||
self.indexID = indexID
|
||||
self.live = live
|
||||
|
Reference in New Issue
Block a user