// The Swift Programming Language // https://docs.swift.org/swift-book import Foundation import ArgumentParser import JellyfinAPI import CryptoKit let SeanutVersion = "0.0.1" @main struct Seanut: AsyncParsableCommand { static var configuration = CommandConfiguration( abstract: "A utility to download media from jellyfin servers", subcommands: [SearchCommand.self, DownloadCommand.self, LoginCommand.self], defaultSubcommand: SearchCommand.self ) struct CommonArguments: ParsableArguments { @Option(name: .shortAndLong, help: "jellyfin server domain name") var domain: String } static func generateJellyfinConfiguration(url: URL) -> JellyfinClient.Configuration { let hostname = Host.current().localizedName ?? "Seanut-Device" let digest = SHA256.hash(data: hostname.data(using: .utf8)!) return JellyfinClient.Configuration( url: url, client: "SeanutSwift", deviceName: hostname, deviceID: digest.compactMap({ String(format: "%02x", $0) }).joined(), version: SeanutVersion ) } static func getAccessToken(client: JellyfinClient, username: String, password: String?) async { let pass: String? = password ?? { print("password>", terminator: " ") return readLine(strippingNewline: true) }() let domain = client.configuration.url.host! do { let auth = try await client.signIn(username: username, password: pass ?? "") let fileName = FileManager.default .homeDirectoryForCurrentUser .appendingPathComponent(".seanut/\(domain)") try auth.accessToken!.write(to: fileName, atomically: false, encoding: .utf8) print("Access token retrieved and saved to ~/.seanut/\(domain)") } catch { fatalError("failed to login with provided credentials. please try again.") } } static func retrieveAccessToken(for domain: URL) -> String? { let tokenPath = FileManager.default .homeDirectoryForCurrentUser .appendingPathComponent(".seanut/\(domain.host!)") return try? String(contentsOf: tokenPath) } }