Wire FAAB schedule-events into master-api and admin IPTV settings, add Android TV Events UI, fix mobile ClearKey/stream playback (DASH + bump 1.0.23), and scaffold the Apple TV (VMCTV) client with docs/scripts.
127 lines
4.8 KiB
Swift
127 lines
4.8 KiB
Swift
import Foundation
|
|
|
|
@MainActor
|
|
final class SessionStore: ObservableObject {
|
|
/// Readable from ApiClient (actor) without MainActor hop.
|
|
nonisolated static let defaultApiBase = "https://master.vonas.nl"
|
|
|
|
@Published var refreshToken: String?
|
|
@Published var apiBase: String
|
|
@Published var profileId: String?
|
|
@Published var profileName: String?
|
|
@Published var profileAvatarId: String?
|
|
@Published var profileShowAvatar: Bool = true
|
|
@Published var profileAvatarUrl: String?
|
|
@Published var iptvFavoriteIds: Set<Int> = []
|
|
@Published var lastIptvStreamId: Int?
|
|
|
|
private let defaults = UserDefaults.standard
|
|
private enum Key {
|
|
static let token = "vmc.tv.refresh_token"
|
|
static let api = "vmc.tv.api_base"
|
|
static let profileId = "vmc.tv.profile_id"
|
|
static let profileName = "vmc.tv.profile_name"
|
|
static let profileAvatar = "vmc.tv.profile_avatar"
|
|
static let profileShowAvatar = "vmc.tv.profile_show_avatar"
|
|
static let profileAvatarUrl = "vmc.tv.profile_avatar_url"
|
|
static let iptvFavorites = "vmc.tv.iptv.favorites"
|
|
static let iptvLastStream = "vmc.tv.iptv.last_stream"
|
|
}
|
|
|
|
init() {
|
|
refreshToken = defaults.string(forKey: Key.token)
|
|
apiBase = defaults.string(forKey: Key.api) ?? Self.defaultApiBase
|
|
profileId = defaults.string(forKey: Key.profileId)
|
|
profileName = defaults.string(forKey: Key.profileName)
|
|
profileAvatarId = defaults.string(forKey: Key.profileAvatar)
|
|
let showRaw = defaults.string(forKey: Key.profileShowAvatar)
|
|
profileShowAvatar = showRaw != "0"
|
|
profileAvatarUrl = defaults.string(forKey: Key.profileAvatarUrl)
|
|
let favs = defaults.array(forKey: Key.iptvFavorites) as? [Int] ?? []
|
|
iptvFavoriteIds = Set(favs)
|
|
let last = defaults.integer(forKey: Key.iptvLastStream)
|
|
lastIptvStreamId = last > 0 ? last : nil
|
|
}
|
|
|
|
var isAuthed: Bool { !(refreshToken ?? "").isEmpty }
|
|
var hasProfile: Bool { !(profileId ?? "").isEmpty }
|
|
|
|
func setToken(_ token: String?) {
|
|
refreshToken = token
|
|
if let token, !token.isEmpty {
|
|
defaults.set(token, forKey: Key.token)
|
|
} else {
|
|
defaults.removeObject(forKey: Key.token)
|
|
setProfile(id: nil, name: nil, avatarId: nil)
|
|
}
|
|
}
|
|
|
|
func setApiBase(_ url: String) {
|
|
let clean = url.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
|
apiBase = clean.isEmpty ? Self.defaultApiBase : clean
|
|
defaults.set(apiBase, forKey: Key.api)
|
|
}
|
|
|
|
func setProfile(
|
|
id: String?,
|
|
name: String?,
|
|
avatarId: String?,
|
|
showAvatar: Bool? = nil,
|
|
avatarUrl: String? = nil
|
|
) {
|
|
profileId = id
|
|
profileName = name
|
|
profileAvatarId = avatarId
|
|
if let id, !id.isEmpty {
|
|
defaults.set(id, forKey: Key.profileId)
|
|
if let name, !name.isEmpty { defaults.set(name, forKey: Key.profileName) }
|
|
else { defaults.removeObject(forKey: Key.profileName) }
|
|
if let avatarId, !avatarId.isEmpty { defaults.set(avatarId, forKey: Key.profileAvatar) }
|
|
else { defaults.removeObject(forKey: Key.profileAvatar) }
|
|
if let showAvatar {
|
|
profileShowAvatar = showAvatar
|
|
defaults.set(showAvatar ? "1" : "0", forKey: Key.profileShowAvatar)
|
|
}
|
|
if let avatarUrl {
|
|
if avatarUrl.isEmpty {
|
|
profileAvatarUrl = nil
|
|
defaults.removeObject(forKey: Key.profileAvatarUrl)
|
|
} else {
|
|
profileAvatarUrl = avatarUrl
|
|
defaults.set(avatarUrl, forKey: Key.profileAvatarUrl)
|
|
}
|
|
}
|
|
} else {
|
|
defaults.removeObject(forKey: Key.profileId)
|
|
defaults.removeObject(forKey: Key.profileName)
|
|
defaults.removeObject(forKey: Key.profileAvatar)
|
|
defaults.removeObject(forKey: Key.profileShowAvatar)
|
|
defaults.removeObject(forKey: Key.profileAvatarUrl)
|
|
profileName = nil
|
|
profileAvatarId = nil
|
|
profileShowAvatar = true
|
|
profileAvatarUrl = nil
|
|
}
|
|
}
|
|
|
|
func isIptvFavorite(_ streamId: Int) -> Bool {
|
|
iptvFavoriteIds.contains(streamId)
|
|
}
|
|
|
|
@discardableResult
|
|
func toggleIptvFavorite(_ streamId: Int) -> Bool {
|
|
if iptvFavoriteIds.contains(streamId) {
|
|
iptvFavoriteIds.remove(streamId)
|
|
} else {
|
|
iptvFavoriteIds.insert(streamId)
|
|
}
|
|
defaults.set(Array(iptvFavoriteIds), forKey: Key.iptvFavorites)
|
|
return iptvFavoriteIds.contains(streamId)
|
|
}
|
|
|
|
func setLastIptvStream(_ streamId: Int) {
|
|
lastIptvStreamId = streamId
|
|
defaults.set(streamId, forKey: Key.iptvLastStream)
|
|
}
|
|
}
|