59 lines
2.2 KiB
Swift
59 lines
2.2 KiB
Swift
import Foundation
|
|
|
|
/// One stem as the widgets draw it. [level] is the fader position (0.8 is unity), [percent] the gain the mixer shows.
|
|
struct WidgetStem: Codable, Hashable, Identifiable {
|
|
var name: String
|
|
var label: String
|
|
var color: UInt32
|
|
var level: Double
|
|
var percent: Int
|
|
var muted = false
|
|
var solo = false
|
|
var id: String { name }
|
|
}
|
|
|
|
/// What's playing and how it's mixed, written by the app for the widgets and the lock screen control.
|
|
struct WidgetSnapshot: Codable, Equatable {
|
|
var title = ""
|
|
var artist = ""
|
|
/// The current mix ("Karaoke", "Custom Mix"…), empty unless stems are playing.
|
|
var mix = ""
|
|
/// What the Mix button moves to.
|
|
var next = ""
|
|
var playing = false
|
|
/// Empty unless stems are playing.
|
|
var stems: [WidgetStem] = []
|
|
/// The art key the saved artwork belongs to, nil while it isn't saved yet.
|
|
var art: String?
|
|
|
|
var hasTrack: Bool { !title.isEmpty }
|
|
var hasStems: Bool { !stems.isEmpty }
|
|
/// Muted, or left out by another stem's solo.
|
|
func silent(_ s: WidgetStem) -> Bool { s.muted || stems.contains(where: \.solo) && !s.solo }
|
|
}
|
|
|
|
enum WidgetStore {
|
|
static let group = Bundle.main.object(forInfoDictionaryKey: "StemifyAppGroup") as? String ?? "group.org.cdnutter.stemify"
|
|
static let mixerKind = "org.cdnutter.stemify.mixer"
|
|
static let mixKind = "org.cdnutter.stemify.mix"
|
|
static let controlKind = "org.cdnutter.stemify.mix-control"
|
|
private static let key = "widgetSnapshot"
|
|
|
|
/// Without the App Group (some sideloading tools drop it) this is a private store and the widgets show Nothing Playing.
|
|
private static var defaults: UserDefaults? { UserDefaults(suiteName: group) }
|
|
|
|
static var artURL: URL? {
|
|
FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: group)?.appendingPathComponent("art.jpg")
|
|
}
|
|
|
|
static func load() -> WidgetSnapshot {
|
|
guard let d = defaults?.data(forKey: key), let s = try? JSONDecoder().decode(WidgetSnapshot.self, from: d) else { return WidgetSnapshot() }
|
|
return s
|
|
}
|
|
|
|
static func save(_ s: WidgetSnapshot) {
|
|
guard let d = try? JSONEncoder().encode(s) else { return }
|
|
defaults?.set(d, forKey: key)
|
|
}
|
|
}
|