Skip to content
Draft
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
138 changes: 77 additions & 61 deletions Amperfy/Screens/Base.lproj/Main.storyboard

Large diffs are not rendered by default.

92 changes: 91 additions & 1 deletion Amperfy/Screens/ViewController/LoginVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import UIKit
import AmperfyKit
import PromiseKit
import SwiftUI

extension String {
var isHyperTextProtocolProvided: Bool {
Expand All @@ -38,7 +39,8 @@ class LoginVC: UIViewController {
@IBOutlet weak var usernameTF: UITextField!
@IBOutlet weak var passwordTF: UITextField!
@IBOutlet weak var apiSelectorButton: BasicButton!

@IBOutlet weak var advancedButton: BasicButton!

@IBAction func serverUrlActionPressed() {
serverUrlTF.resignFirstResponder()
login()
Expand Down Expand Up @@ -124,6 +126,94 @@ class LoginVC: UIViewController {
self.updateApiSelectorText()
})
])

struct ContentView: View {
@Binding var listEntries: [String]
@Environment(
\.dismiss
) var dismiss

var body: some View {
NavigationView {
List {
ForEach(
listEntries.indices,
id: \.self
) {
index in TextField(
"Enter Header: HEADER_NAME=HEADER_VALUE",
text: $listEntries[index]
)
}
.onDelete(
perform: {
(
offset: IndexSet
) in listEntries.remove(
atOffsets: offset
)
})
.moveDisabled(
true
)
}
.navigationBarItems(
leading: Button(
action: {
listEntries
.append(
"New Header"
)
},
label: {
Image(
systemName: "plus"
)
}
),
trailing: HStack {
EditButton()
Button("Back") {
dismiss()
}
}
)
}
}
}
let customHeadersBinding = Binding<[String]>(
get: {
self.backendApi.customHeaders
},
set: {
self.backendApi.setCustomHeaders(
headers: $0
)
}
)
let contentView = ContentView(
listEntries: customHeadersBinding
)
let hostingVC = UIHostingController(
rootView: contentView
)

advancedButton.showsMenuAsPrimaryAction = true
advancedButton.menu = UIMenu(
children: [
UIAction(
title: "Custom HTTP Headers",
handler: { _ in
self.present(
hostingVC,
animated: true,
completion: nil
)
hostingVC.modalPresentationStyle = .formSheet
}
)
]
)
}

override func viewIsAppearing(_ animated: Bool) {
Expand Down
7 changes: 7 additions & 0 deletions AmperfyKit/Api/Ampache/AmpacheApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ class AmpacheApi: BackendApi {
return ampacheXmlServerApi.isStreamingTranscodingActive
}

public var customHeaders: [String] {
return ampacheXmlServerApi.customHeaders
}

func provideCredentials(credentials: LoginCredentials) {
ampacheXmlServerApi.provideCredentials(credentials: credentials)
}
Expand Down Expand Up @@ -88,4 +92,7 @@ class AmpacheApi: BackendApi {
return ampacheXmlServerApi.cleanse(url: url)
}

func setCustomHeaders(headers: [String]) {
ampacheXmlServerApi.setCustomHeaders(headers: headers)
}
}
17 changes: 14 additions & 3 deletions AmperfyKit/Api/Ampache/AmpacheXmlServerApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class AmpacheXmlServerApi: URLCleanser {
var isStreamingTranscodingActive: Bool {
return persistentStorage.settings.streamingFormatPreference != .raw
}

var customHeaders: [String] = []

private let log = OSLog(subsystem: "Amperfy", category: "Ampache")
private let performanceMonitor: ThreadPerformanceMonitor
let eventLogger: EventLogger
Expand Down Expand Up @@ -687,8 +688,14 @@ class AmpacheXmlServerApi: URLCleanser {
}

private func request(url: URL) -> Promise<APIDataResponse> {
let httpHeaders: HTTPHeaders = customHeaders.reduce(into: [:]) { result, header in
let parts = header.split(separator: "=")
if parts.count == 2 {
result[String(parts[0])] = String(parts[1])
}
}
return firstly {
AF.request(url, method: .get).validate().responseData()
AF.request(url, method: .get, headers: httpHeaders).validate().responseData()
}.then { data, response in
Promise<APIDataResponse>.value(APIDataResponse(data: data, url: url, meta: response))
}
Expand Down Expand Up @@ -797,5 +804,9 @@ class AmpacheXmlServerApi: URLCleanser {
self.request(url: url)
}
}


func setCustomHeaders(headers: [String]) {
self.customHeaders = headers
}

}
2 changes: 2 additions & 0 deletions AmperfyKit/Api/BackendApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ public protocol BackendApi: URLCleanser {
var clientApiVersion: String { get }
var serverApiVersion: String { get }
var isStreamingTranscodingActive: Bool { get }
var customHeaders: [String] { get }
func provideCredentials(credentials: LoginCredentials)
func isAuthenticationValid(credentials: LoginCredentials) -> Promise<Void>
func generateUrl(forDownloadingPlayable playable: AbstractPlayable) -> Promise<URL>
Expand All @@ -189,4 +190,5 @@ public protocol BackendApi: URLCleanser {
func createLibrarySyncer(storage: PersistentStorage) -> LibrarySyncer
func createArtworkArtworkDownloadDelegate() -> DownloadManagerDelegate
func extractArtworkInfoFromURL(urlString: String) -> ArtworkRemoteInfo?
func setCustomHeaders(headers: [String])
}
31 changes: 19 additions & 12 deletions AmperfyKit/Api/BackendProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,57 +294,64 @@ public class BackendProxy {
}

extension BackendProxy: BackendApi {

public var clientApiVersion: String {
return activeApi.clientApiVersion
}

public var serverApiVersion: String {
return activeApi.serverApiVersion
}

public var isStreamingTranscodingActive: Bool {
return activeApi.isStreamingTranscodingActive
}


public var customHeaders: [String] {
return activeApi.customHeaders
}

public func provideCredentials(credentials: LoginCredentials) {
activeApi.provideCredentials(credentials: credentials)
}

public func isAuthenticationValid(credentials: LoginCredentials) -> Promise<Void> {
return activeApi.isAuthenticationValid(credentials: credentials)
}

public func generateUrl(forDownloadingPlayable playable: AbstractPlayable) -> Promise<URL> {
return activeApi.generateUrl(forDownloadingPlayable: playable)
}

public func generateUrl(forStreamingPlayable playable: AbstractPlayable, maxBitrate: StreamingMaxBitratePreference) -> Promise<URL> {
return activeApi.generateUrl(forStreamingPlayable: playable, maxBitrate: maxBitrate)
}

public func generateUrl(forArtwork artwork: Artwork) -> Promise<URL> {
return activeApi.generateUrl(forArtwork: artwork)
}

public func checkForErrorResponse(response: APIDataResponse) -> ResponseError? {
return activeApi.checkForErrorResponse(response: response)
}

public func createLibrarySyncer(storage: PersistentStorage) -> LibrarySyncer {
return activeApi.createLibrarySyncer(storage: storage)
}

public func createArtworkArtworkDownloadDelegate() -> DownloadManagerDelegate {
return activeApi.createArtworkArtworkDownloadDelegate()
}

public func extractArtworkInfoFromURL(urlString: String) -> ArtworkRemoteInfo? {
return activeApi.extractArtworkInfoFromURL(urlString: urlString)
}

public func cleanse(url: URL) -> CleansedURL {
return activeApi.cleanse(url: url)
}


public func setCustomHeaders(headers: [String]) {
activeApi.setCustomHeaders(headers: headers)
}
}
7 changes: 7 additions & 0 deletions AmperfyKit/Api/Subsonic/SubsonicApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ extension SubsonicApi: BackendApi {
return subsonicServerApi.isStreamingTranscodingActive
}

public var customHeaders: [String] {
return subsonicServerApi.customHeaders
}

func provideCredentials(credentials: LoginCredentials) {
subsonicServerApi.provideCredentials(credentials: credentials)
}
Expand Down Expand Up @@ -98,4 +102,7 @@ extension SubsonicApi: BackendApi {
return subsonicServerApi.cleanse(url: url)
}

func setCustomHeaders(headers: [String]) {
subsonicServerApi.setCustomHeaders(headers: headers)
}
}
14 changes: 12 additions & 2 deletions AmperfyKit/Api/Subsonic/SubsonicServerApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class SubsonicServerApi: URLCleanser {
var isStreamingTranscodingActive: Bool {
return persistentStorage.settings.streamingFormatPreference != .raw
}
var customHeaders: [String] = []
var authType: SubsonicApiAuthType = .autoDetect
private var authTypeBasedClientApiVersion: SubsonicVersion {
return self.authType == .legacy ? Self.defaultClientApiVersionPreToken : Self.defaultClientApiVersionWithToken
Expand Down Expand Up @@ -750,13 +751,22 @@ class SubsonicServerApi: URLCleanser {
}

private func request(url: URL) -> Promise<APIDataResponse> {
let httpHeaders: HTTPHeaders = customHeaders.reduce(into: [:]) { result, header in
let parts = header.split(separator: "=")
if parts.count == 2 {
result[String(parts[0])] = String(parts[1])
}
}
return firstly {
AF.request(url, method: .get).validate().responseData()
AF.request(url, method: .get, headers: httpHeaders).validate().responseData()
}.then { data, response in
Promise<APIDataResponse>.value(APIDataResponse(data: data, url: url, meta: response))
}
}


func setCustomHeaders(headers: [String]) {
customHeaders = headers
}
}

extension SubsonicServerApi: SubsonicUrlCreator {
Expand Down