Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add server discovery #85

Merged
merged 3 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 45 additions & 13 deletions JellyfinPlayer tvOS/ConnectToServerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,25 +104,57 @@ struct ConnectToServerView: View {
}
}
} else {
Form {
Section(header: Text("Server Information")) {
TextField("Jellyfin Server URL", text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
Button {
viewModel.connectToServer()
} label: {
HStack {
Text("Connect")
Spacer()
if !viewModel.isLoading {

Form {
Section(header: Text("Server Information")) {
TextField("Jellyfin Server URL", text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
Button {
viewModel.connectToServer()
} label: {
HStack {
Text("Connect")
Spacer()
}
if viewModel.isLoading {
ProgressView()
}
}
if viewModel.isLoading {
.disabled(viewModel.isLoading || uri.isEmpty)
}
Section(header: Text("Local Servers")) {
if self.viewModel.searching {
ProgressView()
}
ForEach(self.viewModel.servers, id: \.id) { server in
Button(action: {
print(server.url)
viewModel.connectToServer(at: server.url)
}, label: {
HStack {
VStack(alignment: .leading) {
Text(server.name)
.font(.headline)
Text(server.host)
.font(.subheadline)
}
Spacer()
Image(systemName: "chevron.forward")
.padding()
}

})
.disabled(viewModel.isLoading)
}
}
.disabled(viewModel.isLoading || uri.isEmpty)
.onAppear(perform: self.viewModel.discoverServers)
}
}
else {
ProgressView()
}
}
}
.padding(.leading, 90)
Expand Down
20 changes: 20 additions & 0 deletions JellyfinPlayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
objects = {

/* Begin PBXBuildFile section */
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A872683142E00D78B61 /* ServerDiscovery.swift */; };
091B5A8B2683142E00D78B61 /* UDPBroadCastConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A882683142E00D78B61 /* UDPBroadCastConnection.swift */; };
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A872683142E00D78B61 /* ServerDiscovery.swift */; };
091B5A8E268315D400D78B61 /* UDPBroadCastConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091B5A882683142E00D78B61 /* UDPBroadCastConnection.swift */; };
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E4267ABD5C005D8AB9 /* MainTabView.swift */; };
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */; };
Expand Down Expand Up @@ -181,6 +185,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
091B5A872683142E00D78B61 /* ServerDiscovery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerDiscovery.swift; sourceTree = "<group>"; };
091B5A882683142E00D78B61 /* UDPBroadCastConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UDPBroadCastConnection.swift; sourceTree = "<group>"; };
3773C07648173CE7FEC083D5 /* Pods-JellyfinPlayer iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.debug.xcconfig"; sourceTree = "<group>"; };
3F905C1D3D3A0C9E13E7A0BC /* Pods_JellyfinPlayer_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
531690E4267ABD5C005D8AB9 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -345,6 +351,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
091B5A852683142E00D78B61 /* ServerLocator */ = {
isa = PBXGroup;
children = (
091B5A872683142E00D78B61 /* ServerDiscovery.swift */,
091B5A882683142E00D78B61 /* UDPBroadCastConnection.swift */,
);
path = ServerLocator;
sourceTree = "<group>";
};
532175392671BCED005491E6 /* ViewModels */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -402,6 +417,7 @@
535870752669D60C00D05A09 /* Shared */ = {
isa = PBXGroup;
children = (
091B5A852683142E00D78B61 /* ServerLocator */,
62EC352A26766657000E9F2D /* Singleton */,
532175392671BCED005491E6 /* ViewModels */,
621338912660106C00A81A2A /* Extensions */,
Expand Down Expand Up @@ -868,6 +884,7 @@
536D3D88267C17350004248C /* PublicUserButton.swift in Sources */,
62E632EA267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */,
536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */,
091B5A8E268315D400D78B61 /* UDPBroadCastConnection.swift in Sources */,
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
62EC3530267666A5000E9F2D /* SessionManager.swift in Sources */,
531690F7267ACC00005D8AB9 /* LandscapeItemElement.swift in Sources */,
Expand All @@ -885,6 +902,7 @@
62E632DD267D2E130063E547 /* LibrarySearchViewModel.swift in Sources */,
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */,
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
62E632E4267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
Expand Down Expand Up @@ -936,6 +954,7 @@
532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */,
5377CC01263B596B003A4E83 /* Model.xcdatamodeld in Sources */,
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */,
091B5A8B2683142E00D78B61 /* UDPBroadCastConnection.swift in Sources */,
6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */,
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */,
62E632E9267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */,
Expand All @@ -948,6 +967,7 @@
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */,
62E632E3267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */,
62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */,
62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */,
Expand Down
28 changes: 28 additions & 0 deletions JellyfinPlayer/ConnectToServerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,34 @@ struct ConnectToServerView: View {
}
.disabled(viewModel.isLoading || uri.isEmpty)
}

Section(header: Text("Local Servers")) {
if self.viewModel.searching {
ProgressView()
}
ForEach(self.viewModel.servers, id: \.id) { server in
Button(action: {
print(server.url)
viewModel.connectToServer(at: server.url)
}, label: {
HStack {
VStack {
Text(server.name)
.font(.headline)
Text(server.host)
.font(.subheadline)

}
Spacer()
if viewModel.isLoading {
ProgressView()
}
}

})
}
}
.onAppear(perform: self.viewModel.discoverServers)
}
}
}
Expand Down
92 changes: 92 additions & 0 deletions Shared/ServerLocator/ServerDiscovery.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// ServerLocator.swift
// ABJC
//
// Created by Noah Kamara on 26.03.21.
//

import Foundation

public class ServerDiscovery {
public struct ServerCredential: Codable {
public let host: String
public let port: Int
public let username: String
public let password: String
public let deviceId: String

public init(_ host: String, _ port: Int, _ username: String, _ password: String, _ deviceId: String = UUID().uuidString) {
self.host = host
self.port = port
self.username = username
self.password = password
self.deviceId = deviceId
}
}

public struct ServerLookupResponse: Codable, Hashable, Identifiable {

public func hash(into hasher: inout Hasher) {
return hasher.combine(id)
}

private let address: String
public let id: String
public let name: String

public var url: URL {
URL(string: self.address)!
}
public var host: String {
let components = URLComponents(string: self.address)
if let host = components?.host {
return host
}
return self.address
}

public var port: Int {
let components = URLComponents(string: self.address)
if let port = components?.port {
return port
}
return 8096
}

enum CodingKeys: String, CodingKey {
case address = "Address"
case id = "Id"
case name = "Name"
}
}
private let broadcastConn: UDPBroadcastConnection

public init() {
func receiveHandler(_ ipAddress: String, _ port: Int, _ response: Data) {
print("RECIEVED \(ipAddress):\(String(port)) \(response)")
}

func errorHandler(error: UDPBroadcastConnection.ConnectionError) {
print(error)
}
self.broadcastConn = try! UDPBroadcastConnection(port: 7359, handler: receiveHandler, errorHandler: errorHandler)
}

public func locateServer(completion: @escaping (ServerLookupResponse?) -> Void) {
func receiveHandler(_ ipAddress: String, _ port: Int, _ data: Data) {
do {
let response = try JSONDecoder().decode(ServerLookupResponse.self, from: data)
completion(response)
} catch {
print(error)
completion(nil)
}
}
self.broadcastConn.handler = receiveHandler
do {
try broadcastConn.sendBroadcast("Who is JellyfinServer?")
} catch {
print(error)
}
}
}
Loading