90 lines
4 KiB
Swift
90 lines
4 KiB
Swift
//
|
|
// JellyfinClientConfiguration.swift
|
|
//
|
|
// Copyright 2026 Brendan Szymanski <hello@bscubed.dev>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
import Foundation
|
|
import LuminateCore
|
|
|
|
/// Everything a ``JellyfinClient`` needs to reach one server as one client identity.
|
|
///
|
|
/// The identity fields end up in the `Authorization` header that ``AuthenticationMiddleware``
|
|
/// builds. They are not cosmetic: the server lists ``deviceName`` in its active-devices UI and
|
|
/// keys the session by ``deviceID``.
|
|
package struct JellyfinClientConfiguration: Sendable, Hashable {
|
|
/// The base URL of the Jellyfin server, or `nil` before a server is chosen.
|
|
package var serverURL: URL?
|
|
/// The application name reported to the server.
|
|
package var clientName: String
|
|
/// The human-readable device name shown in the server's active-devices list.
|
|
package var deviceName: String
|
|
/// The stable per-machine device identifier the server keys its session by.
|
|
package var deviceID: String
|
|
/// The application version reported to the server.
|
|
package var version: String
|
|
/// The access token, or `nil` before sign-in.
|
|
package var accessToken: String?
|
|
|
|
/// Creates a configuration, defaulting the client identity to Luminate's.
|
|
///
|
|
/// - Parameters:
|
|
/// - serverURL: The base URL of the Jellyfin server, or `nil` before a server is chosen.
|
|
/// - clientName: The application name reported to the server.
|
|
/// - deviceName: The device name shown by the server; defaults to the machine's host name.
|
|
/// - deviceID: The device identifier; defaults to ``defaultDeviceID()``.
|
|
/// - version: The application version reported to the server.
|
|
/// - accessToken: An access token to start authenticated with, if one was persisted.
|
|
package init(
|
|
serverURL: URL?,
|
|
clientName: String = AppInfo.name,
|
|
deviceName: String = ProcessInfo.processInfo.hostName,
|
|
deviceID: String = JellyfinClientConfiguration.defaultDeviceID(),
|
|
version: String = AppInfo.version,
|
|
accessToken: String? = nil
|
|
) {
|
|
self.serverURL = serverURL
|
|
self.clientName = clientName
|
|
self.deviceName = deviceName
|
|
self.deviceID = deviceID
|
|
self.version = version
|
|
self.accessToken = accessToken
|
|
}
|
|
|
|
/// Derives a device identifier that survives application restarts.
|
|
///
|
|
/// Jellyfin opens a new server-side session for every unfamiliar device identifier, so a value
|
|
/// regenerated each launch would litter the server's device list. The machine ID is stable,
|
|
/// already present on every systemd and D-Bus system, and needs no persistence layer of
|
|
/// Luminate's own.
|
|
///
|
|
/// - Returns: The contents of `/etc/machine-id`, else `/var/lib/dbus/machine-id`, else a fresh
|
|
/// UUID string when neither file is readable or both are empty.
|
|
package static func defaultDeviceID() -> String {
|
|
let candidates = ["/etc/machine-id", "/var/lib/dbus/machine-id"]
|
|
for candidate in candidates {
|
|
guard let contents = try? String(contentsOf: URL(filePath: candidate), encoding: .utf8) else {
|
|
continue
|
|
}
|
|
let trimmed = contents.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if !trimmed.isEmpty {
|
|
return trimmed
|
|
}
|
|
}
|
|
return UUID().uuidString
|
|
}
|
|
}
|