mirror of
https://github.com/yattee/yattee.git
synced 2025-08-06 10:44:06 +00:00
Videos cache model
This commit is contained in:
@@ -41,10 +41,8 @@ final class AccountsModel: ObservableObject {
|
||||
return piped
|
||||
case .invidious:
|
||||
return invidious
|
||||
case .peerTube:
|
||||
return peerTube
|
||||
default:
|
||||
return nil
|
||||
return peerTube
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import Foundation
|
||||
import SwiftyJSON
|
||||
import Logging
|
||||
|
||||
struct CacheModel {
|
||||
static var shared = CacheModel()
|
||||
static let bookmarksGroup = "group.stream.yattee.app.bookmarks"
|
||||
|
||||
let logger = Logger(label: "stream.yattee.cache")
|
||||
|
||||
static let bookmarksGroup = "group.stream.yattee.app.bookmarks"
|
||||
let bookmarksDefaults = UserDefaults(suiteName: Self.bookmarksGroup)
|
||||
|
||||
func removeAll() {
|
37
Model/Cache/VideosCacheModel.swift
Normal file
37
Model/Cache/VideosCacheModel.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
import Cache
|
||||
import Foundation
|
||||
import Logging
|
||||
import SwiftyJSON
|
||||
|
||||
struct VideosCacheModel {
|
||||
static let shared = VideosCacheModel()
|
||||
let logger = Logger(label: "stream.yattee.cache.videos")
|
||||
|
||||
static let jsonToDataTransformer: (JSON) -> Data = { try! $0.rawData() }
|
||||
static let jsonFromDataTransformer: (Data) -> JSON = { try! JSON(data: $0) }
|
||||
static let jsonTransformer = Transformer(toData: jsonToDataTransformer, fromData: jsonFromDataTransformer)
|
||||
|
||||
static let videosStorageDiskConfig = DiskConfig(name: "videos")
|
||||
static let vidoesStorageMemoryConfig = MemoryConfig()
|
||||
|
||||
let videosStorage = try! Storage<String, JSON>(
|
||||
diskConfig: Self.videosStorageDiskConfig,
|
||||
memoryConfig: Self.vidoesStorageMemoryConfig,
|
||||
transformer: Self.jsonTransformer
|
||||
)
|
||||
|
||||
func storeVideo(_ video: Video) {
|
||||
logger.info("caching \(video.cacheKey)")
|
||||
try? videosStorage.setObject(video.json, forKey: video.cacheKey)
|
||||
}
|
||||
|
||||
func retrieveVideo(_ cacheKey: String) -> Video? {
|
||||
logger.info("retrieving cache for \(cacheKey)")
|
||||
|
||||
if let json = try? videosStorage.object(forKey: cacheKey) {
|
||||
return Video.from(json)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@@ -109,4 +109,18 @@ struct Channel: Identifiable, Hashable {
|
||||
guard contentType != .videos, contentType != .playlists else { return true }
|
||||
return tabs.contains { $0.contentType == contentType }
|
||||
}
|
||||
|
||||
var json: JSON {
|
||||
[
|
||||
"id": id,
|
||||
"name": name
|
||||
]
|
||||
}
|
||||
|
||||
static func from(_ json: JSON) -> Self {
|
||||
.init(
|
||||
id: json["id"].stringValue,
|
||||
name: json["name"].stringValue
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,11 @@ extension PlayerModel {
|
||||
return
|
||||
}
|
||||
|
||||
if let video = VideosCacheModel.shared.retrieveVideo(watch.video.cacheKey) {
|
||||
historyVideos.append(video)
|
||||
return
|
||||
}
|
||||
|
||||
guard let api = playerAPI(watch.video) else { return }
|
||||
|
||||
api.video(watch.videoID)
|
||||
@@ -28,6 +33,7 @@ extension PlayerModel {
|
||||
guard let self else { return }
|
||||
|
||||
if let video: Video = response.typedContent() {
|
||||
VideosCacheModel.shared.storeVideo(video)
|
||||
self.historyVideos.append(video)
|
||||
}
|
||||
}
|
||||
|
@@ -87,7 +87,7 @@ extension PlayerModel {
|
||||
}
|
||||
|
||||
func playerAPI(_ video: Video) -> VideosAPI! {
|
||||
guard let url = video.instanceURL else { return nil }
|
||||
guard let url = video.instanceURL else { return accounts.api }
|
||||
switch video.app {
|
||||
case .local:
|
||||
return nil
|
||||
|
@@ -34,6 +34,7 @@ extension PlayerModel {
|
||||
.load()
|
||||
.onSuccess { response in
|
||||
if let video: Video = response.typedContent() {
|
||||
VideosCacheModel.shared.storeVideo(video)
|
||||
guard video.videoID == self.currentVideo?.videoID else {
|
||||
self.logger.info("ignoring loaded streams from \(instance.description) as current video has changed")
|
||||
return
|
||||
|
@@ -36,4 +36,18 @@ struct Thumbnail {
|
||||
self.url = url
|
||||
self.quality = quality
|
||||
}
|
||||
|
||||
var json: JSON {
|
||||
[
|
||||
"url": url.absoluteString,
|
||||
"quality": quality.rawValue
|
||||
]
|
||||
}
|
||||
|
||||
static func from(_ json: JSON) -> Self {
|
||||
.init(
|
||||
url: URL(string: json["url"].stringValue)!,
|
||||
quality: .init(rawValue: json["quality"].stringValue) ?? .default
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -119,6 +119,71 @@ struct Video: Identifiable, Equatable, Hashable {
|
||||
)
|
||||
}
|
||||
|
||||
var cacheKey: String {
|
||||
switch app {
|
||||
case .local:
|
||||
return videoID
|
||||
case .invidious:
|
||||
return "youtube-\(videoID)"
|
||||
case .piped:
|
||||
return "youtube-\(videoID)"
|
||||
case .peerTube:
|
||||
return "peertube-\(instanceURL?.absoluteString ?? "unknown-instance")-\(videoID)"
|
||||
}
|
||||
}
|
||||
|
||||
var json: JSON {
|
||||
let dateFormatter = ISO8601DateFormatter()
|
||||
let publishedAt = self.publishedAt == nil ? "" : dateFormatter.string(from: self.publishedAt!)
|
||||
return [
|
||||
"instanceID": instanceID ?? "",
|
||||
"app": app.rawValue,
|
||||
"instanceURL": instanceURL?.absoluteString ?? "",
|
||||
"id": id,
|
||||
"videoID": videoID,
|
||||
"videoURL": videoURL?.absoluteString ?? "",
|
||||
"title": title,
|
||||
"author": author,
|
||||
"length": length,
|
||||
"published": published,
|
||||
"views": views,
|
||||
"description": description ?? "",
|
||||
"genre": genre ?? "",
|
||||
"channel": channel.json.object,
|
||||
"thumbnails": thumbnails.compactMap { $0.json.object },
|
||||
"indexID": indexID ?? "",
|
||||
"live": live,
|
||||
"upcoming": upcoming,
|
||||
"publishedAt": publishedAt
|
||||
]
|
||||
}
|
||||
|
||||
static func from(_ json: JSON) -> Self {
|
||||
let dateFormatter = ISO8601DateFormatter()
|
||||
|
||||
return Video(
|
||||
instanceID: json["instanceID"].stringValue,
|
||||
app: .init(rawValue: json["app"].stringValue) ?? AccountsModel.shared.current.app ?? .local,
|
||||
instanceURL: URL(string: json["instanceURL"].stringValue) ?? AccountsModel.shared.current.instance.apiURL,
|
||||
id: json["id"].stringValue,
|
||||
videoID: json["videoID"].stringValue,
|
||||
videoURL: json["videoURL"].url,
|
||||
title: json["title"].stringValue,
|
||||
author: json["author"].stringValue,
|
||||
length: json["length"].doubleValue,
|
||||
published: json["published"].stringValue,
|
||||
views: json["views"].intValue,
|
||||
description: json["description"].string,
|
||||
genre: json["genre"].string,
|
||||
channel: Channel.from(json["channel"]),
|
||||
thumbnails: json["thumbnails"].arrayValue.compactMap { Thumbnail.from($0) },
|
||||
indexID: json["indexID"].stringValue,
|
||||
live: json["live"].boolValue,
|
||||
upcoming: json["upcoming"].boolValue,
|
||||
publishedAt: dateFormatter.date(from: json["publishedAt"].stringValue)
|
||||
)
|
||||
}
|
||||
|
||||
var instance: Instance! {
|
||||
if let instance = InstancesModel.shared.find(instanceID) {
|
||||
return instance
|
||||
|
@@ -91,12 +91,12 @@ extension Watch {
|
||||
var video: Video {
|
||||
let url = URL(string: videoID)
|
||||
|
||||
if app == nil || !Video.VideoID.isValid(videoID) {
|
||||
if !Video.VideoID.isValid(videoID) {
|
||||
if let url {
|
||||
return .local(url)
|
||||
}
|
||||
}
|
||||
|
||||
return Video(app: app, instanceURL: instanceURL, videoID: videoID)
|
||||
return Video(app: app ?? AccountsModel.shared.current.app ?? .local, instanceURL: instanceURL, videoID: videoID)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user