This commit is contained in:
Ethan Pippin 2022-08-04 00:05:50 -06:00
parent 9e28d05d87
commit 5975c83354
14 changed files with 585 additions and 75 deletions

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@ -11,6 +11,14 @@
0919395722C35C1400392D55 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0919395622C35C1400392D55 /* Assets.xcassets */; };
0919395A22C35C1400392D55 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0919395822C35C1400392D55 /* Main.storyboard */; };
0924531822D5BC7E00B2E710 /* ffmpeg LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = 0924531622D5BC7E00B2E710 /* ffmpeg LICENSE.txt */; };
E174A306289B755C0092450E /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = E174A305289B755C0092450E /* LaunchAtLogin */; };
E174A312289B84F90092450E /* PreferencesViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E174A311289B84F90092450E /* PreferencesViewController.storyboard */; };
E174A314289B855C0092450E /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E174A313289B855C0092450E /* PreferencesViewController.swift */; };
E174A318289B86820092450E /* PreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E174A317289B86820092450E /* PreferencesWindow.swift */; };
E174A31B289B8D700092450E /* ActionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E174A31A289B8D700092450E /* ActionManager.swift */; };
E174A31F289B92480092450E /* AlertViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E174A31E289B92480092450E /* AlertViewController.swift */; };
E174A322289B929F0092450E /* AlertViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E174A321289B929F0092450E /* AlertViewController.storyboard */; };
E19E5327289B932300A94983 /* AlertWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19E5326289B932300A94983 /* AlertWindow.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -33,6 +41,13 @@
0924531622D5BC7E00B2E710 /* ffmpeg LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "ffmpeg LICENSE.txt"; sourceTree = "<group>"; };
09A23D5B2468FB9B0071B5A9 /* Jellyfin Server.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Jellyfin Server.app"; sourceTree = BUILT_PRODUCTS_DIR; };
09A23D5C2468FBAB0071B5A9 /* Server.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Server.entitlements; sourceTree = "<group>"; };
E174A311289B84F90092450E /* PreferencesViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = PreferencesViewController.storyboard; sourceTree = "<group>"; };
E174A313289B855C0092450E /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
E174A317289B86820092450E /* PreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindow.swift; sourceTree = "<group>"; };
E174A31A289B8D700092450E /* ActionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionManager.swift; sourceTree = "<group>"; };
E174A31E289B92480092450E /* AlertViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertViewController.swift; sourceTree = "<group>"; };
E174A321289B929F0092450E /* AlertViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = AlertViewController.storyboard; sourceTree = "<group>"; };
E19E5326289B932300A94983 /* AlertWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertWindow.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -40,6 +55,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E174A306289B755C0092450E /* LaunchAtLogin in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -60,6 +76,9 @@
children = (
09A23D5C2468FBAB0071B5A9 /* Server.entitlements */,
0919395222C35C1400392D55 /* AppDelegate.swift */,
E174A31A289B8D700092450E /* ActionManager.swift */,
E174A320289B926C0092450E /* AlertWindow */,
E174A319289B8D520092450E /* PreferencesWindow */,
0919395622C35C1400392D55 /* Assets.xcassets */,
0919395822C35C1400392D55 /* Main.storyboard */,
0919395B22C35C1400392D55 /* Info.plist */,
@ -67,6 +86,26 @@
path = "Jellyfin Server";
sourceTree = "<group>";
};
E174A319289B8D520092450E /* PreferencesWindow */ = {
isa = PBXGroup;
children = (
E174A317289B86820092450E /* PreferencesWindow.swift */,
E174A313289B855C0092450E /* PreferencesViewController.swift */,
E174A311289B84F90092450E /* PreferencesViewController.storyboard */,
);
path = PreferencesWindow;
sourceTree = "<group>";
};
E174A320289B926C0092450E /* AlertWindow */ = {
isa = PBXGroup;
children = (
E19E5326289B932300A94983 /* AlertWindow.swift */,
E174A31E289B92480092450E /* AlertViewController.swift */,
E174A321289B929F0092450E /* AlertViewController.storyboard */,
);
path = AlertWindow;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -84,6 +123,9 @@
dependencies = (
);
name = "Jellyfin Server";
packageProductDependencies = (
E174A305289B755C0092450E /* LaunchAtLogin */,
);
productName = Server;
productReference = 09A23D5B2468FB9B0071B5A9 /* Jellyfin Server.app */;
productType = "com.apple.product-type.application";
@ -120,6 +162,9 @@
Base,
);
mainGroup = 0919394622C35C1300392D55;
packageReferences = (
E174A304289B755C0092450E /* XCRemoteSwiftPackageReference "LaunchAtLogin" */,
);
productRefGroup = 0919394622C35C1300392D55;
projectDirPath = "";
projectRoot = "";
@ -135,7 +180,9 @@
buildActionMask = 2147483647;
files = (
0924531822D5BC7E00B2E710 /* ffmpeg LICENSE.txt in Resources */,
E174A322289B929F0092450E /* AlertViewController.storyboard in Resources */,
0919395722C35C1400392D55 /* Assets.xcassets in Resources */,
E174A312289B84F90092450E /* PreferencesViewController.storyboard in Resources */,
0919395A22C35C1400392D55 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -147,7 +194,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E19E5327289B932300A94983 /* AlertWindow.swift in Sources */,
0919395322C35C1400392D55 /* AppDelegate.swift in Sources */,
E174A318289B86820092450E /* PreferencesWindow.swift in Sources */,
E174A314289B855C0092450E /* PreferencesViewController.swift in Sources */,
E174A31F289B92480092450E /* AlertViewController.swift in Sources */,
E174A31B289B8D700092450E /* ActionManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -285,10 +337,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "Jellyfin Server/Server.entitlements";
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 6YC6EAV7ES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "Jellyfin Server/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
@ -299,7 +351,7 @@
MARKETING_VERSION = 10.5.5;
PRODUCT_BUNDLE_IDENTIFIER = Jellyfin.Server;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = JellyfinMacProvision;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Debug;
@ -309,10 +361,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "Jellyfin Server/Server.entitlements";
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_STYLE = Manual;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = 6YC6EAV7ES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "Jellyfin Server/Info.plist";
LD_RUNPATH_SEARCH_PATHS = (
@ -323,7 +375,7 @@
MARKETING_VERSION = 10.5.5;
PRODUCT_BUNDLE_IDENTIFIER = Jellyfin.Server;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = JellyfinMacProvision;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Release;
@ -350,6 +402,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
E174A304289B755C0092450E /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin";
requirement = {
branch = main;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
E174A305289B755C0092450E /* LaunchAtLogin */ = {
isa = XCSwiftPackageProductDependency;
package = E174A304289B755C0092450E /* XCRemoteSwiftPackageReference "LaunchAtLogin" */;
productName = LaunchAtLogin;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 0919394722C35C1300392D55 /* Project object */;
}

View File

@ -0,0 +1,46 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
//
import Foundation
import AppKit
enum ActionManager {
static let shareFolder: URL = {
FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".local/share")
}()
static func launchWebUI() {
NSWorkspace.shared.open(.init(string: "http://localhost:8096")!)
}
static func showLogs() {
let logFolder = shareFolder.appendingPathComponent("jellyfin/log")
NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: logFolder.path)
}
static func launchAbout() {
NSWorkspace.shared.open(.init(string: "https://jellyfin.org")!)
}
static func restart() {
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "sleep 2; open \"\(Bundle.main.bundlePath)\""]
task.launch()
NSApp.terminate(self)
exit(0)
}
static func terminateWithError() {
NSApp.terminate(self)
exit(1)
}
}

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Alert View Controller-->
<scene sceneID="PEd-7d-5j0">
<objects>
<viewController storyboardIdentifier="AlertViewController" id="bU7-R8-ocO" customClass="AlertViewController" customModule="Jellyfin_Server" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="tOy-S4-hL0">
<rect key="frame" x="0.0" y="0.0" width="356" height="76"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fdY-Da-lII">
<rect key="frame" x="111" y="3" width="134" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="r62-wU-Ttb"/>
<constraint firstAttribute="height" constant="20" id="scZ-eY-Uyg"/>
</constraints>
<buttonCell key="cell" type="push" title="Exit" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Png-uI-fVN">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<color key="bezelColor" name="linkColor" catalog="System" colorSpace="catalog"/>
<connections>
<action selector="exitSelected:" target="bU7-R8-ocO" id="fhS-Ze-Fpf"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="XLz-Rj-HcW">
<rect key="frame" x="18" y="50" width="320" height="16"/>
<textFieldCell key="cell" selectable="YES" alignment="center" title="Error" id="sZe-fP-Ack">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="XLz-Rj-HcW" firstAttribute="top" secondItem="tOy-S4-hL0" secondAttribute="top" constant="10" id="DeW-Aw-WxI"/>
<constraint firstAttribute="trailing" secondItem="XLz-Rj-HcW" secondAttribute="trailing" constant="20" id="Dme-Ed-gzb"/>
<constraint firstAttribute="bottom" secondItem="fdY-Da-lII" secondAttribute="bottom" constant="10" id="OAI-ie-nUf"/>
<constraint firstItem="fdY-Da-lII" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="aA3-Kr-sGp"/>
<constraint firstItem="XLz-Rj-HcW" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="kI9-sR-LeJ"/>
<constraint firstItem="fdY-Da-lII" firstAttribute="top" secondItem="XLz-Rj-HcW" secondAttribute="bottom" constant="20" id="os9-F6-xid"/>
<constraint firstItem="XLz-Rj-HcW" firstAttribute="leading" secondItem="tOy-S4-hL0" secondAttribute="leading" constant="20" id="xhS-PT-ux0"/>
</constraints>
</view>
<connections>
<outlet property="errorLabel" destination="XLz-Rj-HcW" id="bcD-0u-mgC"/>
</connections>
</viewController>
<customObject id="9uD-mB-xHs" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="12" y="67"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,19 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
//
import AppKit
class AlertViewController: NSViewController {
@IBOutlet weak var errorLabel: NSTextField!
@IBAction func exitSelected(_ sender: Any) {
view.window?.windowController?.close()
ActionManager.terminateWithError()
}
}

View File

@ -0,0 +1,28 @@
//
// AlertWindow.swift
// Jellyfin Server
//
// Created by Ethan Pippin on 8/3/22.
// Copyright © 2022 Jellyfin. All rights reserved.
//
import AppKit
class AlertWindow: NSWindow {
convenience init(text: String) {
let storyboard = NSStoryboard(name: "AlertViewController", bundle: nil)
let alertViewController = storyboard.instantiateController(withIdentifier: "AlertViewController") as! AlertViewController
self.init(contentViewController: alertViewController)
alertViewController.errorLabel.stringValue = text
styleMask.remove(.fullScreen)
styleMask.remove(.miniaturizable)
styleMask.remove(.resizable)
titlebarAppearsTransparent = true
title = ""
level = .floating
}
}

View File

@ -1,89 +1,109 @@
//
// AppDelegate.swift
// Server
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Created by Anthony Lavado on 2019-06-26.
// Copyright © 2019 Anthony Lavado. All rights reserved.
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
//
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import AppKit
import Cocoa
import LaunchAtLogin
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
let bundle = Bundle.main
var task = Process()
private let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
private var windowController = NSWindowController(window: nil)
private var jellyfinTask = Process()
func applicationDidFinishLaunching(_ aNotification: Notification) {
if let button = statusItem.button {
button.image = NSImage(named:NSImage.Name("StatusBarButtonImage"))
}
statusItem.button?.image = NSImage(named: "StatusBarButtonImage")
let fileManager = FileManager.default
let home = FileManager.default.homeDirectoryForCurrentUser
let appFolder = home.appendingPathComponent(".local/share")
var isDirectory: ObjCBool = false
let folderExists = fileManager.fileExists(atPath: appFolder.path,
isDirectory: &isDirectory)
if !folderExists || !isDirectory.boolValue {
do {
try fileManager.createDirectory(at: appFolder,
withIntermediateDirectories: true,
attributes: nil)
} catch {
// TODO: Add system dialog and stop here. Provide instructions at a link.
print("Could not create cache folder. Reason: \(error)")
}
}
let path = Bundle.main.path(forAuxiliaryExecutable: "jellyfin")
let webui = Bundle.main.resourceURL!.appendingPathComponent("jellyfin-web").path
task.launchPath = path
task.arguments = ["--webdir", webui]
do {
try task.run()
} catch {
// TODO: Add system dialog and stop here. Provide instructions at a link.
print("Could not launch Jellyfin. Reason: \(error)")
}
constructMenu()
createAppFolder()
startJellyfinTask()
createStatusBarMenu()
}
func applicationWillTerminate(_ aNotification: Notification) {
task.terminate()
task.waitUntilExit()
print("app will terminate received")
jellyfinTask.terminate()
jellyfinTask.waitUntilExit()
}
@objc func launchWebUI(sender: Any?) {
NSWorkspace.shared.open(NSURL(string: "http://localhost:8096")! as URL)
private func createAppFolder() {
var isDirectory: ObjCBool = false
let shareFolderExists = FileManager.default.fileExists(atPath: ActionManager.shareFolder.path,
isDirectory: &isDirectory)
if !shareFolderExists || !isDirectory.boolValue {
do {
try FileManager.default.createDirectory(at: ActionManager.shareFolder, withIntermediateDirectories: true)
} catch {
let alertWindow = AlertWindow(text: "Jellyfin Server was unable to properly create expected directories.")
windowController.window = alertWindow
windowController.showWindow(self)
}
}
}
@objc func showLogs(sender: Any?){
NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: "/Users/\(NSUserName())/.local/share/jellyfin/log")
private func startJellyfinTask() {
let jellyfinPath = Bundle.main.path(forAuxiliaryExecutable: "jellyfin")
let webUIPath = Bundle.main.resourceURL!.appendingPathComponent("jellyfin-web").path
guard let jellyfinPath = jellyfinPath else {
let alertWindow = AlertWindow(text: "Jellyfin Server was unable to start underlying jellyfin task.")
windowController.window = alertWindow
windowController.showWindow(self)
return
}
jellyfinTask.launchPath = jellyfinPath
jellyfinTask.arguments = ["--webdir", webUIPath]
do {
try jellyfinTask.run()
} catch {
let alertWindow = AlertWindow(text: "Jellyfin Server was unable to start underlying jellyfin task.")
windowController.window = alertWindow
windowController.showWindow(self)
}
}
func constructMenu() {
private func createStatusBarMenu() {
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Launch Web UI", action: #selector(launchWebUI(sender:)), keyEquivalent: "l"))
menu.addItem(NSMenuItem(title: "Show Logs", action: #selector(showLogs(sender:)), keyEquivalent: "d"))
menu.addItem(withTitle: "Launch", action: #selector(launchWebUI), keyEquivalent: "l")
menu.addItem(withTitle: "Show Logs", action: #selector(showLogs), keyEquivalent: "d")
menu.addItem(withTitle: "Restart", action: #selector(restart), keyEquivalent: "r")
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit Jellyfin Server", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
menu.addItem(withTitle: "Preferences", action: #selector(launchPreferences), keyEquivalent: ",")
menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = menu
}
}
@objc private func launchWebUI() {
ActionManager.launchAbout()
}
@objc private func showLogs() {
ActionManager.showLogs()
}
@objc private func restart() {
ActionManager.restart()
}
@objc private func launchPreferences() {
let window = PreferencesWindow()
windowController.window = window
windowController.showWindow(self)
}
}

View File

@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

@ -0,0 +1,82 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "jellyfin-banner-light.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "jellyfin-banner-dark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@ -27,7 +27,7 @@
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 Jellyfin Contributors. All rights reserved.</string>
<string>Copyright © 2022 Jellyfin Contributors. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Preferences View Controller-->
<scene sceneID="PEd-7d-5j0">
<objects>
<viewController storyboardIdentifier="PreferencesViewController" id="bU7-R8-ocO" customClass="PreferencesViewController" customModule="Jellyfin_Server" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="tOy-S4-hL0">
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xgB-oQ-tF1">
<rect key="frame" x="25" y="220" width="300" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="M6H-N2-AA3"/>
<constraint firstAttribute="width" constant="300" id="jt1-AU-FH4"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="jellyfin-banner" id="e4N-CF-Dic"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qEc-pa-mey">
<rect key="frame" x="73" y="194" width="204" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="gGl-zS-3e9"/>
<constraint firstAttribute="width" constant="200" id="kS1-0S-mjy"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Version 1.2.3" id="YHH-EB-Hiz">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="a23-zM-ylI">
<rect key="frame" x="-2" y="10" width="354" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Copyright © 2022 Jellyfin" id="5VS-Kj-kIo">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iV1-fg-N8A">
<rect key="frame" x="113" y="157" width="123" height="18"/>
<buttonCell key="cell" type="check" title="Launch at Login" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Az4-Xj-0Mi">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gxh-eI-cDG">
<rect key="frame" x="108" y="111" width="134" height="32"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="120" id="8x3-gy-6H5"/>
<constraint firstAttribute="width" constant="120" id="Ahg-po-3td"/>
<constraint firstAttribute="height" constant="20" id="xQy-q3-kdD"/>
</constraints>
<buttonCell key="cell" type="push" title="Launch Web UI" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wE7-Nw-pDc">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="launchWebUISelected:" target="bU7-R8-ocO" id="Y1q-ze-oTU"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hE0-YK-gRX">
<rect key="frame" x="108" y="81" width="134" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="3kk-Pb-tWB"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="120" id="QAT-4c-DkD"/>
<constraint firstAttribute="width" constant="120" id="z2X-7N-gUG"/>
</constraints>
<buttonCell key="cell" type="push" title="Show Logs" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6WI-yj-06V">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showLogsSelected:" target="bU7-R8-ocO" id="9jx-gg-Oqp"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3SL-fH-pl4">
<rect key="frame" x="108" y="51" width="134" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="DNG-Ny-2hn"/>
<constraint firstAttribute="width" constant="120" id="LPR-XR-Lo7"/>
</constraints>
<buttonCell key="cell" type="push" title="About" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Em3-6n-hE4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="aboutSelected:" target="bU7-R8-ocO" id="EcU-RU-LdB"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="3SL-fH-pl4" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="0AP-KV-I7n"/>
<constraint firstItem="a23-zM-ylI" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="0BQ-0k-2Gx"/>
<constraint firstItem="iV1-fg-N8A" firstAttribute="top" secondItem="qEc-pa-mey" secondAttribute="bottom" constant="20" id="Gv8-P6-0ne"/>
<constraint firstItem="qEc-pa-mey" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="HfN-me-yw6"/>
<constraint firstItem="iV1-fg-N8A" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="Q7v-Pw-ydB"/>
<constraint firstItem="3SL-fH-pl4" firstAttribute="top" secondItem="hE0-YK-gRX" secondAttribute="bottom" constant="10" id="Rcw-3x-JSh"/>
<constraint firstItem="qEc-pa-mey" firstAttribute="top" secondItem="xgB-oQ-tF1" secondAttribute="bottom" constant="10" id="RlL-zN-L9n"/>
<constraint firstItem="xgB-oQ-tF1" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="TXg-MQ-QtM"/>
<constraint firstItem="gxh-eI-cDG" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="Xak-9M-JR1"/>
<constraint firstAttribute="bottom" secondItem="a23-zM-ylI" secondAttribute="bottom" constant="10" id="Ymx-ng-4SZ"/>
<constraint firstItem="gxh-eI-cDG" firstAttribute="top" secondItem="iV1-fg-N8A" secondAttribute="bottom" constant="20" id="bNt-p3-nGR"/>
<constraint firstItem="hE0-YK-gRX" firstAttribute="top" secondItem="gxh-eI-cDG" secondAttribute="bottom" constant="10" id="jbR-nE-v6k"/>
<constraint firstItem="xgB-oQ-tF1" firstAttribute="top" secondItem="tOy-S4-hL0" secondAttribute="top" id="lqW-1I-Euc"/>
<constraint firstItem="hE0-YK-gRX" firstAttribute="centerX" secondItem="tOy-S4-hL0" secondAttribute="centerX" id="o2u-2j-CKZ"/>
<constraint firstItem="a23-zM-ylI" firstAttribute="leading" secondItem="tOy-S4-hL0" secondAttribute="leading" id="ueC-AA-dgU"/>
<constraint firstAttribute="trailing" secondItem="a23-zM-ylI" secondAttribute="trailing" id="v7l-OO-Upb"/>
</constraints>
</view>
<connections>
<outlet property="launchAtLoginToggle" destination="iV1-fg-N8A" id="LnK-p3-p5s"/>
</connections>
</viewController>
<customObject id="9uD-mB-xHs" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="91" y="124"/>
</scene>
</scenes>
<resources>
<image name="jellyfin-banner" width="1481" height="466"/>
</resources>
</document>

View File

@ -0,0 +1,30 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
//
import AppKit
class PreferencesViewController: NSViewController {
@IBOutlet weak var launchAtLoginToggle: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func launchWebUISelected(_ sender: Any) {
ActionManager.launchWebUI()
}
@IBAction func showLogsSelected(_ sender: Any) {
ActionManager.showLogs()
}
@IBAction func aboutSelected(_ sender: Any) {
ActionManager.launchAbout()
}
}

View File

@ -0,0 +1,26 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
//
import AppKit
class PreferencesWindow: NSWindow {
convenience init() {
let storyboard = NSStoryboard(name: "PreferencesViewController", bundle: nil)
let preferencesViewController = storyboard.instantiateController(withIdentifier: "PreferencesViewController") as! PreferencesViewController
self.init(contentViewController: preferencesViewController)
styleMask.remove(.fullScreen)
styleMask.remove(.miniaturizable)
styleMask.remove(.resizable)
titlebarAppearsTransparent = true
title = ""
level = .floating
}
}