// The Swift Programming Language // https://docs.swift.org/swift-book import Foundation import ArgumentParser import JellyfinAPI import CryptoKit import Logging 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 ) static var Logger: Logger? = nil struct CommonArguments: ParsableArguments { @Option(name: .shortAndLong, help: "jellyfin server domain name") var domain: String @Flag(name: .long, help: "prints extra information to the console") var verbose = false } static func maybeSetLogger(_ opts: CommonArguments) { Seanut.Logger = opts.verbose ? Logging.Logger(label: "garden.focks.SeanutSwift.logger") : nil } 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 ☑️") } catch { print("Failed to login with provided credentials. please try again later.") exit(withError: error) } } static func retrieveAccessToken(for domain: URL) -> String? { let tokenPath = FileManager.default .homeDirectoryForCurrentUser .appendingPathComponent(".seanut/\(domain.host!)") return try? String(contentsOf: tokenPath) } }