feat: load optional XDG TOML configuration with session overrides

This commit is contained in:
2026-10-11 14:41:11 +02:00
parent 0dce073287
commit 722a225da5
16 changed files with 461 additions and 89 deletions
+2 -6
View File
@@ -9,7 +9,6 @@ final class CompactWindowController: NSObject {
var snappingEnabled: Bool {
didSet {
guard snappingEnabled != oldValue else { return }
defaults.set(snappingEnabled, forKey: "compactSnappingEnabled")
onChange?()
}
}
@@ -45,7 +44,6 @@ final class CompactWindowController: NSObject {
private enum FullScreenTransition { case entering, exiting }
private let defaults: UserDefaults
private weak var window: NSWindow?
private var snapshot: Snapshot?
private var fullScreenTransition: FullScreenTransition?
@@ -57,10 +55,8 @@ final class CompactWindowController: NSObject {
private var videoAspect = 16.0 / 9.0
private var fittedAspect: Double?
init(defaults: UserDefaults = .standard) {
self.defaults = defaults
defaults.register(defaults: ["compactSnappingEnabled": true])
snappingEnabled = defaults.bool(forKey: "compactSnappingEnabled")
init(snappingEnabled: Bool = true) {
self.snappingEnabled = snappingEnabled
super.init()
}
+28 -11
View File
@@ -1,9 +1,11 @@
import AppKit
import SwiftUI
import TerebiConfiguration
@MainActor
final class PlayerModel: ObservableObject {
static let shared = PlayerModel()
static var startupConfiguration = PlayerConfiguration()
static let shared = PlayerModel(configuration: startupConfiguration)
@Published private(set) var state = PlayerState()
@Published private(set) var fileName: String?
@@ -24,7 +26,7 @@ final class PlayerModel: ObservableObject {
@Published private(set) var controlsVisible = true
@Published private(set) var isFullScreen = false
@Published private(set) var isOnTop = false
let compactWindow = CompactWindowController()
let compactWindow: CompactWindowController
var isCompact: Bool { compactWindow.isCompact }
var canToggleCompact: Bool {
isCompact || compactWindow.isTransitioning ||
@@ -51,7 +53,6 @@ final class PlayerModel: ObservableObject {
private var youtubeTask: Task<Void, Never>?
private var youtubeRequest: UUID?
private var resolvingAutomaticQuality = false
private let defaults: UserDefaults
private let qualityClock: () -> Double
private var automaticQuality = AutomaticQuality()
private var qualityNow: Double { qualityClock() }
@@ -62,11 +63,12 @@ final class PlayerModel: ObservableObject {
private var lastSavedPosition: Double?
private var systemPlayback: SystemPlaybackControls?
init(defaults: UserDefaults = .standard, qualityClock: @escaping () -> Double = { ProcessInfo.processInfo.systemUptime }) {
self.defaults = defaults
init(defaults: UserDefaults = .standard, configuration: PlayerConfiguration = PlayerConfiguration(),
qualityClock: @escaping () -> Double = { ProcessInfo.processInfo.systemUptime }) {
self.qualityClock = qualityClock
automaticYouTubeQuality = defaults.bool(forKey: "automaticYouTubeQuality")
showsBufferInfo = defaults.bool(forKey: "showsBufferInfo")
automaticYouTubeQuality = configuration.automaticQuality
showsBufferInfo = configuration.showBufferInfo
compactWindow = CompactWindowController(snappingEnabled: configuration.snapToEdges)
playbackProgress = PlaybackProgress(defaults: defaults)
let player = player_create()
handle = player
@@ -162,13 +164,11 @@ final class PlayerModel: ObservableObject {
func setShowsBufferInfo(_ enabled: Bool) {
showsBufferInfo = enabled
defaults.set(enabled, forKey: "showsBufferInfo")
revealControls()
}
func setAutomaticYouTubeQuality(_ enabled: Bool) {
automaticYouTubeQuality = enabled
defaults.set(enabled, forKey: "automaticYouTubeQuality")
automaticQuality = AutomaticQuality()
if !enabled && resolvingAutomaticQuality { cancelYouTubeResolution() }
revealControls()
@@ -1370,7 +1370,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(openDocuments(_:withReplyEvent:)),
forEventClass: AEEventClass(kCoreEventClass), andEventID: AEEventID(kAEOpenDocuments))
let arguments = Array(CommandLine.arguments.dropFirst())
if arguments.count == 2, arguments[0] == "--open-media", let url = URL(string: arguments[1]) {
if let index = arguments.firstIndex(of: "--open-media"), index + 1 < arguments.count,
let url = URL(string: arguments[index + 1]) {
PlayerModel.shared.open(url)
}
}
@@ -1448,8 +1449,24 @@ struct TerebiApp: App {
#if !PLAYER_CHECK
@_cdecl("terebi_app_main")
#endif
@MainActor
public func terebiAppMain() {
do {
PlayerModel.startupConfiguration = try PlayerConfiguration.loadForApplication(
arguments: Array(CommandLine.arguments.dropFirst()),
environment: ProcessInfo.processInfo.environment,
homeDirectory: FileManager.default.homeDirectoryForCurrentUser)
} catch {
FileHandle.standardError.write(Data("terebi: \(error.localizedDescription)\n".utf8))
NSApplication.shared.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
let alert = NSAlert()
alert.alertStyle = .critical
alert.messageText = "Could not load terebi configuration"
alert.informativeText = error.localizedDescription
alert.runModal()
exit(78)
}
TerebiApp.main()
}
#endif