mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-22 06:46:46 +01:00
feat(tvos): Add TopShelf Extension (#1561)
This commit is contained in:
112
modules/top-shelf-cache/ios/TopShelfCacheModule.swift
Normal file
112
modules/top-shelf-cache/ios/TopShelfCacheModule.swift
Normal file
@@ -0,0 +1,112 @@
|
||||
import ExpoModulesCore
|
||||
import Foundation
|
||||
import Security
|
||||
#if canImport(TVServices)
|
||||
import TVServices
|
||||
#endif
|
||||
|
||||
public class TopShelfCacheModule: Module {
|
||||
private let appGroupInfoPlistKey = "StreamyfinAppGroupIdentifier"
|
||||
private let keychainAccessGroupInfoPlistKey = "StreamyfinKeychainAccessGroupIdentifier"
|
||||
private let cacheKey = "TopShelfCache"
|
||||
private let apiKeyService = "StreamyfinTopShelf"
|
||||
private let apiKeyAccount = "JellyfinApiKey"
|
||||
private var appGroupIdentifier: String? {
|
||||
if let appGroupIdentifier = Bundle.main.object(
|
||||
forInfoDictionaryKey: appGroupInfoPlistKey
|
||||
) as? String {
|
||||
return appGroupIdentifier
|
||||
}
|
||||
|
||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return "group.\(bundleIdentifier)"
|
||||
}
|
||||
private var keychainAccessGroupIdentifier: String? {
|
||||
Bundle.main.object(forInfoDictionaryKey: keychainAccessGroupInfoPlistKey) as? String
|
||||
}
|
||||
|
||||
public func definition() -> ModuleDefinition {
|
||||
Name("TopShelfCache")
|
||||
|
||||
Function("writeCache") { (json: String, apiKey: String?) -> Bool in
|
||||
guard
|
||||
let appGroupIdentifier = appGroupIdentifier,
|
||||
let defaults = UserDefaults(suiteName: appGroupIdentifier)
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
defaults.set(json, forKey: cacheKey)
|
||||
defaults.set(Date().timeIntervalSince1970, forKey: "\(cacheKey)UpdatedAt")
|
||||
defaults.synchronize()
|
||||
let didSaveAPIKey = saveAPIKey(apiKey)
|
||||
|
||||
#if canImport(TVServices)
|
||||
TVTopShelfContentProvider.topShelfContentDidChange()
|
||||
#endif
|
||||
|
||||
return didSaveAPIKey
|
||||
}
|
||||
|
||||
Function("clearCache") { () -> Bool in
|
||||
guard
|
||||
let appGroupIdentifier = appGroupIdentifier,
|
||||
let defaults = UserDefaults(suiteName: appGroupIdentifier)
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
defaults.removeObject(forKey: cacheKey)
|
||||
defaults.removeObject(forKey: "\(cacheKey)UpdatedAt")
|
||||
defaults.synchronize()
|
||||
let didDeleteAPIKey = deleteAPIKey()
|
||||
|
||||
#if canImport(TVServices)
|
||||
TVTopShelfContentProvider.topShelfContentDidChange()
|
||||
#endif
|
||||
|
||||
return didDeleteAPIKey
|
||||
}
|
||||
}
|
||||
|
||||
private func baseAPIKeyQuery() -> [String: Any] {
|
||||
var query: [String: Any] = [
|
||||
kSecClass as String: kSecClassGenericPassword,
|
||||
kSecAttrService as String: apiKeyService,
|
||||
kSecAttrAccount as String: apiKeyAccount
|
||||
]
|
||||
|
||||
if let keychainAccessGroupIdentifier {
|
||||
query[kSecAttrAccessGroup as String] = keychainAccessGroupIdentifier
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
private func saveAPIKey(_ apiKey: String?) -> Bool {
|
||||
guard deleteAPIKey() else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard
|
||||
let apiKey,
|
||||
!apiKey.isEmpty,
|
||||
let data = apiKey.data(using: .utf8)
|
||||
else {
|
||||
return true
|
||||
}
|
||||
|
||||
var query = baseAPIKeyQuery()
|
||||
query[kSecValueData as String] = data
|
||||
let status = SecItemAdd(query as CFDictionary, nil)
|
||||
return status == errSecSuccess
|
||||
}
|
||||
|
||||
private func deleteAPIKey() -> Bool {
|
||||
let status = SecItemDelete(baseAPIKeyQuery() as CFDictionary)
|
||||
return status == errSecSuccess || status == errSecItemNotFound
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user