[create-pull-request] automated change (#47)

Co-authored-by: acvigue <acvigue@users.noreply.github.com>
This commit is contained in:
github-actions[bot] 2021-06-10 09:28:21 -07:00 committed by GitHub
parent 9ad53092a4
commit b26f81247a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 631 additions and 645 deletions

View File

@ -35,7 +35,7 @@ struct PersistenceController {
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores(completionHandler: { (_, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

View File

@ -16,28 +16,28 @@ struct ConnectToServerView: View {
@EnvironmentObject var globalData: GlobalData
@EnvironmentObject var jsi: justSignedIn
@State private var uri = "";
@State private var isWorking = false;
@State private var isErrored = false;
@State private var isDone = false;
@State private var isSignInErrored = false;
@State private var isConnected = false;
@State private var serverName = "";
@State private var usernameDisabled: Bool = false;
@State private var publicUsers: [UserDto] = [];
@State private var lastPublicUsers: [UserDto] = [];
@State private var username = "";
@State private var password = "";
@State private var server_id = "";
@State private var serverSkipped: Bool = false;
@State private var serverSkippedAlert: Bool = false;
@State private var skip_server_bool: Bool = false;
@State private var skip_server_obj: Server!;
@State private var uri = ""
@State private var isWorking = false
@State private var isErrored = false
@State private var isDone = false
@State private var isSignInErrored = false
@State private var isConnected = false
@State private var serverName = ""
@State private var usernameDisabled: Bool = false
@State private var publicUsers: [UserDto] = []
@State private var lastPublicUsers: [UserDto] = []
@State private var username = ""
@State private var password = ""
@State private var server_id = ""
@State private var serverSkipped: Bool = false
@State private var serverSkippedAlert: Bool = false
@State private var skip_server_bool: Bool = false
@State private var skip_server_obj: Server!
@Binding var rootIsActive: Bool
private var reauthDeviceID: String = "";
private let userUUID = UUID();
private var reauthDeviceID: String = ""
private let userUUID = UUID()
init(skip_server: Bool, skip_server_prefill: Server, reauth_deviceId: String, isActive: Binding<Bool>) {
_rootIsActive = isActive
@ -51,7 +51,7 @@ struct ConnectToServerView: View {
}
func start() {
if(skip_server_bool) {
if skip_server_bool {
uri = skip_server_obj.baseURI!
UserAPI.getPublicUsers()
@ -59,19 +59,19 @@ struct ConnectToServerView: View {
switch completion {
case .finished:
break
case .failure(_):
skip_server_bool = false;
skip_server_obj = Server();
case .failure:
skip_server_bool = false
skip_server_obj = Server()
break
}
}, receiveValue: { response in
publicUsers = response
serverSkipped = true;
serverSkippedAlert = true;
serverSkipped = true
serverSkippedAlert = true
server_id = skip_server_obj.server_id!
serverName = skip_server_obj.name!
isConnected = true;
isConnected = true
})
.store(in: &globalData.pendingAPIRequests)
}
@ -80,12 +80,12 @@ struct ConnectToServerView: View {
func doLogin() {
isWorking = true
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String;
var deviceName = UIDevice.current.name;
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
var deviceName = UIDevice.current.name
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
deviceName = deviceName.removeRegexMatches(pattern: "[^\\w\\s]");
deviceName = deviceName.removeRegexMatches(pattern: "[^\\w\\s]")
let authHeader = "MediaBrowser Client=\"SwiftFin\", Device=\"\(deviceName)\", DeviceId=\"\(serverSkipped ? reauthDeviceID : userUUID.uuidString)\", Version=\"\(appVersion ?? "0.0.1")\"";
let authHeader = "MediaBrowser Client=\"SwiftFin\", Device=\"\(deviceName)\", DeviceId=\"\(serverSkipped ? reauthDeviceID : userUUID.uuidString)\", Version=\"\(appVersion ?? "0.0.1")\""
print(authHeader)
JellyfinAPI.customHeaders["X-Emby-Authorization"] = authHeader
@ -147,17 +147,17 @@ struct ConnectToServerView: View {
var body: some View {
Form {
if(!isConnected) {
if !isConnected {
Section(header: Text("Server Information")) {
TextField("Jellyfin Server URL", text: $uri)
.disableAutocorrection(true)
.autocapitalization(.none)
Button {
isWorking = true;
if(!uri.contains("http")) {
uri = "http://" + uri;
isWorking = true
if !uri.contains("http") {
uri = "http://" + uri
}
if(uri.last == "/") {
if uri.last == "/" {
uri = String(uri.dropLast())
}
@ -167,7 +167,7 @@ struct ConnectToServerView: View {
switch completion {
case .finished:
break
case .failure(_):
case .failure:
isErrored = true
isWorking = false
break
@ -176,15 +176,15 @@ struct ConnectToServerView: View {
let server = response
serverName = server.serverName!
server_id = server.id!
if(server.startupWizardCompleted!) {
isConnected = true;
if server.startupWizardCompleted! {
isConnected = true
UserAPI.getPublicUsers()
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case .failure(_):
case .failure:
isErrored = true
isWorking = false
break
@ -201,7 +201,7 @@ struct ConnectToServerView: View {
HStack {
Text("Connect")
Spacer()
if(isWorking == true) {
if isWorking == true {
ProgressView()
}
}
@ -210,7 +210,7 @@ struct ConnectToServerView: View {
Alert(title: Text("Error"), message: Text("Couldn't connect to server"), dismissButton: .default(Text("Try again")))
}
} else {
if(publicUsers.count == 0) {
if publicUsers.count == 0 {
Section(header: Text("\(serverSkipped ? "Reauthenticate" : "Login") to \(serverName)")) {
TextField("Username", text: $username)
.disableAutocorrection(true)
@ -225,7 +225,7 @@ struct ConnectToServerView: View {
HStack {
Text("Login")
Spacer()
if(isWorking) {
if isWorking {
ProgressView()
}
}
@ -235,8 +235,8 @@ struct ConnectToServerView: View {
}
}
if(serverSkipped) {
Section() {
if serverSkipped {
Section {
Button {
serverSkippedAlert = false
server_id = ""
@ -244,8 +244,8 @@ struct ConnectToServerView: View {
isConnected = false
serverSkipped = false
} label: {
HStack() {
HStack() {
HStack {
HStack {
Image(systemName: "chevron.left")
Text("Change Server")
}
@ -254,13 +254,13 @@ struct ConnectToServerView: View {
}
}
} else {
Section() {
Section {
Button {
publicUsers = lastPublicUsers
usernameDisabled = false;
usernameDisabled = false
} label: {
HStack() {
HStack() {
HStack {
HStack {
Image(systemName: "chevron.left")
Text("Back")
}
@ -272,9 +272,9 @@ struct ConnectToServerView: View {
} else {
Section(header: Text("\(serverSkipped ? "Reauthenticate" : "Login") to \(serverName)")) {
ForEach(publicUsers, id: \.id) { publicUser in
HStack() {
HStack {
Button() {
if(publicUser.hasPassword!) {
if publicUser.hasPassword! {
lastPublicUsers = publicUsers
username = publicUser.name!
usernameDisabled = true
@ -286,10 +286,10 @@ struct ConnectToServerView: View {
doLogin()
}
} label: {
HStack() {
HStack {
Text(publicUser.name!).font(.subheadline).fontWeight(.semibold)
Spacer()
if(publicUser.primaryImageTag != "") {
if publicUser.primaryImageTag != "" {
LazyImage(source: URL(string: "\(uri)/Users/\(publicUser.id!)/Images/Primary?width=200&quality=80&tag=\(publicUser.primaryImageTag!)"))
.contentMode(.aspectFill)
.frame(width: 60, height: 60)
@ -310,13 +310,13 @@ struct ConnectToServerView: View {
}
}
Section() {
Section {
Button() {
lastPublicUsers = publicUsers
publicUsers = []
username = ""
} label: {
HStack() {
HStack {
Text("Other User").font(.subheadline).fontWeight(.semibold)
Spacer()
Image(systemName: "person.fill.questionmark")

View File

@ -40,7 +40,7 @@ struct ContentView: View {
private var recentFilterSet: LibraryFilters = LibraryFilters(filters: [], sortOrder: [.descending], sortBy: ["DateCreated"])
func startup() {
if(viewDidLoad == true) {
if viewDidLoad == true {
return
}
@ -72,9 +72,9 @@ struct ContentView: View {
}
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
var deviceName = UIDevice.current.name;
var deviceName = UIDevice.current.name
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
deviceName = deviceName.removeRegexMatches(pattern: "[^\\w\\s]");
deviceName = deviceName.removeRegexMatches(pattern: "[^\\w\\s]")
var header = "MediaBrowser "
header.append("Client=\"SwiftFin\", ")
@ -98,7 +98,7 @@ struct ContentView: View {
return !(response.configuration?.latestItemsExcludes?.contains(element))!
}
if(loadState == 1) {
if loadState == 1 {
isLoading = false
}
})
@ -113,7 +113,7 @@ struct ContentView: View {
library_names[item.id ?? ""] = item.name
})
if(loadState == 1) {
if loadState == 1 {
isLoading = false
}
})
@ -132,13 +132,13 @@ struct ContentView: View {
}
var body: some View {
if (needsToSelectServer == true) {
if needsToSelectServer == true {
NavigationView {
ConnectToServerView(isActive: $needsToSelectServer)
}
.navigationViewStyle(StackNavigationViewStyle())
.environmentObject(globalData)
} else if (globalData.expiredCredentials == true) {
} else if globalData.expiredCredentials == true {
NavigationView {
ConnectToServerView(skip_server: true, skip_server_prefill: globalData.server,
reauth_deviceId: globalData.user.device_uuid ?? "", isActive: $globalData.expiredCredentials)
@ -148,8 +148,8 @@ struct ContentView: View {
} else {
if !jsi.did {
LoadingView(isShowing: $isLoading) {
VStack() {
if(loadState == 0) {
VStack {
if loadState == 0 {
TabView(selection: $tabSelection) {
NavigationView {
VStack(alignment: .leading) {

View File

@ -30,7 +30,6 @@ struct ProgressBar: Shape {
}
}
struct ContinueWatchingView: View {
@EnvironmentObject var globalData: GlobalData
@ -38,7 +37,7 @@ struct ContinueWatchingView: View {
func onAppear() {
DispatchQueue.global(qos: .userInitiated).async {
ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary,.backdrop,.thumb])
ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary, .backdrop, .thumb])
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
@ -50,9 +49,9 @@ struct ContinueWatchingView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
if(items.count > 0) {
LazyHStack() {
Spacer().frame(width:14)
if items.count > 0 {
LazyHStack {
Spacer().frame(width: 14)
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
@ -70,7 +69,7 @@ struct ContinueWatchingView: View {
.cornerRadius(10)
.overlay(
Group {
if(item.type == "Episode") {
if item.type == "Episode" {
Text("\(item.name!)")
.font(.caption)
.padding(6)

View File

@ -5,7 +5,7 @@
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
*/
//lol can someone buy me a coffee this took forever :|
// lol can someone buy me a coffee this took forever :|
import Foundation
import JellyfinAPI
@ -34,60 +34,60 @@ enum CPUModel {
}
class DeviceProfileBuilder {
public var bitrate: Int = 0;
public var bitrate: Int = 0
public func setMaxBitrate(bitrate: Int) {
self.bitrate = bitrate
}
public func buildProfile() -> DeviceProfile {
let maxStreamingBitrate = bitrate;
let maxStaticBitrate = bitrate;
let musicStreamingTranscodingBitrate = 384000;
let maxStreamingBitrate = bitrate
let maxStaticBitrate = bitrate
let musicStreamingTranscodingBitrate = 384000
//Build direct play profiles
var directPlayProfiles: [DirectPlayProfile] = [];
// Build direct play profiles
var directPlayProfiles: [DirectPlayProfile] = []
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav", videoCodec: "h264", type: .video)]
//Device supports Dolby Digital (AC3, EAC3)
if(supportsFeature(minimumSupported: .A8X)) {
if(supportsFeature(minimumSupported: .A10)) {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav,ac3,eac3,flac,opus", videoCodec: "hevc,h264,hev1", type: .video)] //HEVC/H.264 with Dolby Digital
// Device supports Dolby Digital (AC3, EAC3)
if supportsFeature(minimumSupported: .A8X) {
if supportsFeature(minimumSupported: .A10) {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav,ac3,eac3,flac,opus", videoCodec: "hevc,h264,hev1", type: .video)] // HEVC/H.264 with Dolby Digital
} else {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "ac3,eac3,aac,mp3,wav,opus", videoCodec: "h264", type: .video)] //H.264 with Dolby Digital
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "ac3,eac3,aac,mp3,wav,opus", videoCodec: "h264", type: .video)] // H.264 with Dolby Digital
}
}
//Device supports Dolby Vision?
if(supportsFeature(minimumSupported: .A10X)) {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav,ac3,eac3,flac,opus", videoCodec: "dvhe,dvh1,dva1,dvav,h264,hevc,hev1", type: .video)] //H.264/HEVC with Dolby Digital - No Atmos - Vision
// Device supports Dolby Vision?
if supportsFeature(minimumSupported: .A10X) {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav,ac3,eac3,flac,opus", videoCodec: "dvhe,dvh1,dva1,dvav,h264,hevc,hev1", type: .video)] // H.264/HEVC with Dolby Digital - No Atmos - Vision
}
//Device supports Dolby Atmos?
if(supportsFeature(minimumSupported: .A12)) {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav,ac3,eac3,flac,truehd,dts,dca,opus", videoCodec: "h264,hevc,dvhe,dvh1,dva1,dvav,h264,hevc,hev1", type: .video)] //H.264/HEVC with Dolby Digital & Atmos - Vision
// Device supports Dolby Atmos?
if supportsFeature(minimumSupported: .A12) {
directPlayProfiles = [DirectPlayProfile(container: "mov,mp4,mkv", audioCodec: "aac,mp3,wav,ac3,eac3,flac,truehd,dts,dca,opus", videoCodec: "h264,hevc,dvhe,dvh1,dva1,dvav,h264,hevc,hev1", type: .video)] // H.264/HEVC with Dolby Digital & Atmos - Vision
}
//Build transcoding profiles
var transcodingProfiles: [TranscodingProfile] = [];
// Build transcoding profiles
var transcodingProfiles: [TranscodingProfile] = []
transcodingProfiles = [TranscodingProfile(container: "ts", type: .video, videoCodec: "h264", audioCodec: "aac,mp3,wav")]
//Device supports Dolby Digital (AC3, EAC3)
if(supportsFeature(minimumSupported: .A8X)) {
if(supportsFeature(minimumSupported: .A10)) {
// Device supports Dolby Digital (AC3, EAC3)
if supportsFeature(minimumSupported: .A8X) {
if supportsFeature(minimumSupported: .A10) {
transcodingProfiles = [TranscodingProfile(container: "ts", type: .video, videoCodec: "aac,mp3,wav,eac3,ac3,flac,opus", audioCodec: "h264,hevc,hev1", _protocol: "hls", context: .streaming, maxAudioChannels: "6", minSegments: 2, breakOnNonKeyFrames: true)]
} else {
transcodingProfiles = [TranscodingProfile(container: "ts", type: .video, videoCodec: "h264", audioCodec: "aac,mp3,wav,eac3,ac3,opus", _protocol: "hls", context: .streaming, maxAudioChannels: "6", minSegments: 2, breakOnNonKeyFrames: true)]
}
}
//Device supports Dolby Vision?
if(supportsFeature(minimumSupported: .A10X)) {
// Device supports Dolby Vision?
if supportsFeature(minimumSupported: .A10X) {
transcodingProfiles = [TranscodingProfile(container: "ts", type: .video, videoCodec: "dva1,dvav,dvhe,dvh1,hevc,h264,hev1", audioCodec: "aac,mp3,wav,ac3,eac3,flac,opus", _protocol: "hls", context: .streaming, maxAudioChannels: "6", minSegments: 2, breakOnNonKeyFrames: true)]
}
//Device supports Dolby Atmos?
if(supportsFeature(minimumSupported: .A12)) {
// Device supports Dolby Atmos?
if supportsFeature(minimumSupported: .A12) {
transcodingProfiles = [TranscodingProfile(container: "ts", type: .video, videoCodec: "dva1,dvav,dvhe,dvh1,hevc,h264,hev1", audioCodec: "aac,mp3,wav,ac3,eac3,flac,dts,truehd,dca,opus", _protocol: "hls", context: .streaming, maxAudioChannels: "6", minSegments: 2, breakOnNonKeyFrames: true)]
}
@ -106,8 +106,8 @@ class DeviceProfileBuilder {
codecProfiles.append(CodecProfile(type: .video, applyConditions: h264CodecConditions, codec: "h264"))
if(supportsFeature(minimumSupported: .A10)) {
codecProfiles.append(CodecProfile(type: .video, applyConditions: hevcCodecConditions,codec: "hevc"))
if supportsFeature(minimumSupported: .A10) {
codecProfiles.append(CodecProfile(type: .video, applyConditions: hevcCodecConditions, codec: "hevc"))
}
var subtitleProfiles: [SubtitleProfile] = []
@ -147,7 +147,7 @@ class DeviceProfileBuilder {
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8 , value != 0 else { return identifier }
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
#endif
@ -189,10 +189,10 @@ class DeviceProfileBuilder {
case "iPad7,1", "iPad7,2": return .A10X
case "iPad7,3", "iPad7,4": return .A10X
case "iPad7,5", "iPad7,6", "iPad7,11", "iPad7,12": return .A10
case "iPad8,1", "iPad8,2" ,"iPad8,3", "iPad8,4": return .A12X
case "iPad8,5", "iPad8,6" ,"iPad8,7", "iPad8,8": return .A12X
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return .A12X
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return .A12X
case "iPad8,9", "iPad8,10", "iPad8,11", "iPad8,12": return .A12Z
case "iPad11,3", "iPad11,4" ,"iPad11,6", "iPad11,7": return .A12
case "iPad11,3", "iPad11,4", "iPad11,6", "iPad11,7": return .A12
case "iPad13,1", "iPad13,2": return .A14
case "AppleTV5,3": return .A8
case "AppleTV6,2": return .A10X

View File

@ -200,7 +200,7 @@ struct EpisodeItemView: View {
HStack {
Spacer().frame(width: 16)
ForEach(item.people!, id: \.self) { person in
if(person.type! == "Actor") {
if person.type! == "Actor" {
NavigationLink(destination: LazyView {
LibraryView(withPerson: person)
}) {
@ -399,7 +399,7 @@ struct EpisodeItemView: View {
HStack {
Spacer().frame(width: 16)
ForEach(item.people!, id: \.self) { person in
if(person.type! == "Actor") {
if person.type! == "Actor" {
NavigationLink(destination: LazyView {
LibraryView(withPerson: person)
}) {

View File

@ -10,18 +10,18 @@ import Introspect
import JellyfinAPI
class VideoPlayerItem: ObservableObject {
@Published var shouldShowPlayer: Bool = false;
@Published var itemToPlay: BaseItemDto = BaseItemDto();
@Published var shouldShowPlayer: Bool = false
@Published var itemToPlay: BaseItemDto = BaseItemDto()
}
struct ItemView: View {
@EnvironmentObject private var globalData: GlobalData
private var item: BaseItemDto;
private var item: BaseItemDto
@StateObject private var videoPlayerItem: VideoPlayerItem = VideoPlayerItem()
@State private var videoIsLoading: Bool = false; //This variable is only changed by the underlying VLC view.
@State private var isLoading: Bool = false;
@State private var viewDidLoad: Bool = false;
@State private var videoIsLoading: Bool = false; // This variable is only changed by the underlying VLC view.
@State private var isLoading: Bool = false
@State private var viewDidLoad: Bool = false
init(item: BaseItemDto) {
self.item = item
@ -29,7 +29,7 @@ struct ItemView: View {
var body: some View {
VStack {
if(videoPlayerItem.shouldShowPlayer) {
if videoPlayerItem.shouldShowPlayer {
LoadingViewNoBlur(isShowing: $videoIsLoading) {
VLCPlayerWithControls(item: videoPlayerItem.itemToPlay, loadBinding: $videoIsLoading, pBinding: _videoPlayerItem.projectedValue.shouldShowPlayer)
}.navigationBarHidden(true)
@ -42,13 +42,13 @@ struct ItemView: View {
.supportedOrientations(.landscape)
} else {
VStack {
if(item.type == "Movie") {
if item.type == "Movie" {
MovieItemView(item: item)
} else if(item.type == "Season") {
} else if item.type == "Season" {
SeasonItemView(item: item)
} else if(item.type == "Series") {
} else if item.type == "Series" {
SeriesItemView(item: item)
} else if(item.type == "Episode") {
} else if item.type == "Episode" {
EpisodeItemView(item: item)
} else {
Text("Type: \(item.type ?? "") not implemented yet :(")

View File

@ -20,7 +20,7 @@ class OrientationInfo: ObservableObject {
case landscape
}
@Published var orientation: Orientation = .portrait;
@Published var orientation: Orientation = .portrait
private var _observer: NSObjectProtocol?
@ -31,8 +31,7 @@ class OrientationInfo: ObservableObject {
}
if device.orientation.isPortrait {
self?.orientation = .portrait
}
else if device.orientation.isLandscape {
} else if device.orientation.isLandscape {
self?.orientation = .landscape
}
}
@ -52,7 +51,7 @@ extension View {
}
struct HostingWindowFinder: UIViewRepresentable {
var callback: (UIWindow?) -> ()
var callback: (UIWindow?) -> Void
func makeUIView(context: Context) -> UIView {
let view = UIView()
@ -134,13 +133,13 @@ class PreferenceUIHostingController: UIHostingController<AnyView> {
public var _orientations: UIInterfaceOrientationMask = .allButUpsideDown {
didSet {
UIViewController.attemptRotationToDeviceOrientation();
if(_orientations == .landscape) {
let value = UIInterfaceOrientation.landscapeRight.rawValue;
UIViewController.attemptRotationToDeviceOrientation()
if _orientations == .landscape {
let value = UIInterfaceOrientation.landscapeRight.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
}
};
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
_orientations
}
@ -149,7 +148,7 @@ class PreferenceUIHostingController: UIHostingController<AnyView> {
didSet {
overrideUserInterfaceStyle = _viewPreference
}
};
}
}
extension View {
@ -180,7 +179,7 @@ struct JellyfinPlayerApp: App {
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(OrientationInfo())
.environmentObject(jsi)
.withHostingWindow() { window in
.withHostingWindow { window in
window?.rootViewController = PreferenceUIHostingController(wrappedView: ContentView().environment(\.managedObjectContext, persistenceController.container.viewContext).environmentObject(OrientationInfo()).environmentObject(jsi))
}
}

View File

@ -13,21 +13,21 @@ struct LatestMediaView: View {
@EnvironmentObject var globalData: GlobalData
@State var items: [BaseItemDto] = []
private var library_id: String = "";
@State private var viewDidLoad: Bool = false;
private var library_id: String = ""
@State private var viewDidLoad: Bool = false
init(usingParentID: String) {
library_id = usingParentID;
library_id = usingParentID
}
func onAppear() {
if(viewDidLoad == true) {
if viewDidLoad == true {
return
}
viewDidLoad = true;
viewDidLoad = true
DispatchQueue.global(qos: .userInitiated).async {
UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], enableUserData: true, limit: 12)
UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], enableUserData: true, limit: 12)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
@ -39,13 +39,13 @@ struct LatestMediaView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack() {
Spacer().frame(width:16)
LazyHStack {
Spacer().frame(width: 16)
ForEach(items, id: \.id) { item in
if(item.type == "Series" || item.type == "Movie") {
if item.type == "Series" || item.type == "Movie" {
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
Spacer().frame(height:10)
Spacer().frame(height: 10)
LazyImage(source: item.getSeriesPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100))
.placeholderAndFailure {
Image(uiImage: UIImage(blurHash: item.getSeriesPrimaryImageBlurHash(), size: CGSize(width: 16, height: 20))!)
@ -55,7 +55,7 @@ struct LatestMediaView: View {
}
.frame(width: 100, height: 150)
.cornerRadius(10)
Spacer().frame(height:5)
Spacer().frame(height: 5)
Text(item.seriesName ?? item.name ?? "")
.font(.caption)
.fontWeight(.semibold)

View File

@ -13,7 +13,7 @@ struct LibraryListView: View {
@State var library_ids: [String] = ["favorites", "genres"]
@State var library_names: [String: String] = ["favorites": "Favorites", "genres": "Genres"]
var libraries: [String: String] = [:] //input libraries
var libraries: [String: String] = [:] // input libraries
var withFavorites: LibraryFilters = LibraryFilters(filters: [.isFavorite], sortOrder: [.descending], sortBy: ["SortName"])
init(libraries: [String: String]) {
@ -21,8 +21,8 @@ struct LibraryListView: View {
}
func onAppear() {
if(library_ids.count == 2) {
libraries.forEach() { k,v in
if library_ids.count == 2 {
libraries.forEach { k, v in
print("\(k): \(v)")
_library_ids.wrappedValue.append(k)
_library_names.wrappedValue[k] = v

View File

@ -32,7 +32,7 @@ struct LibrarySearchView: View {
isLoading = true
DispatchQueue.global(qos: .userInitiated).async {
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], includeItemTypes: ["Movie", "Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
@ -43,7 +43,7 @@ struct LibrarySearchView: View {
}
}
//MARK: tracks for grid
// MARK: tracks for grid
@State private var tracks: [GridItem] = []
func recalcTracks() {
let trkCnt = Int(floor(UIScreen.main.bounds.size.width / 125))
@ -57,12 +57,12 @@ struct LibrarySearchView: View {
VStack {
Spacer().frame(height: 6)
SearchBar(text: $searchQuery)
if(isLoading == true) {
if isLoading == true {
Spacer()
ProgressView()
Spacer()
} else {
if(!items.isEmpty) {
if !items.isEmpty {
ScrollView(.vertical) {
Spacer().frame(height: 16)
LazyVGrid(columns: tracks) {
@ -105,7 +105,7 @@ struct LibrarySearchView: View {
.onAppear(perform: onAppear)
.navigationBarTitle("Search", displayMode: .inline)
.onChange(of: searchQuery) { query in
if(CACurrentMediaTime() - lastSearchTime > 0.5) {
if CACurrentMediaTime() - lastSearchTime > 0.5 {
lastSearchTime = CACurrentMediaTime()
requestSearch(query: query)
}
@ -113,4 +113,4 @@ struct LibrarySearchView: View {
}
}
//stream NM5 by nicki!
// stream NM5 by nicki!

View File

@ -24,10 +24,10 @@ struct LibraryView: View {
var genre: String = ""
var studio: String = ""
@State private var totalPages: Int = 0;
@State private var currentPage: Int = 0;
@State private var isSearching: String? = "";
@State private var viewDidLoad: Bool = false;
@State private var totalPages: Int = 0
@State private var currentPage: Int = 0
@State private var isSearching: String? = ""
@State private var viewDidLoad: Bool = false
init(usingParentID: String, title: String) {
self.usingParentID = usingParentID
@ -61,7 +61,7 @@ struct LibraryView: View {
func onAppear() {
recalcTracks()
if(viewDidLoad) {
if viewDidLoad {
return
}
@ -69,7 +69,7 @@ struct LibraryView: View {
items = []
DispatchQueue.global(qos: .userInitiated).async {
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: (personId == "" ? nil : [personId]), studioIds: (studio == "" ? nil : [studio]), genreIds: (genre == "" ? nil : [genre]), enableImages: true)
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], includeItemTypes: ["Movie", "Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: (personId == "" ? nil : [personId]), studioIds: (studio == "" ? nil : [studio]), genreIds: (genre == "" ? nil : [genre]), enableImages: true)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
isLoading = false
@ -84,7 +84,7 @@ struct LibraryView: View {
}
}
//MARK: tracks for grid
// MARK: tracks for grid
@State private var tracks: [GridItem] = []
func recalcTracks() {
let trkCnt = Int(floor(UIScreen.main.bounds.size.width / 125))
@ -96,10 +96,10 @@ struct LibraryView: View {
var body: some View {
ZStack {
if(isLoading == true) {
if isLoading == true {
ProgressView()
} else {
if(!items.isEmpty) {
if !items.isEmpty {
VStack {
ScrollView(.vertical) {
Spacer().frame(height: 16)
@ -132,10 +132,10 @@ struct LibraryView: View {
}.onChange(of: orientationInfo.orientation) { _ in
recalcTracks()
}
if(totalPages > 1) {
HStack() {
if totalPages > 1 {
HStack {
Spacer()
HStack() {
HStack {
Button {
currentPage = currentPage - 1
onAppear()
@ -169,7 +169,7 @@ struct LibraryView: View {
.navigationBarTitle(title, displayMode: .inline)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
if(currentPage > 0) {
if currentPage > 0 {
Button {
currentPage = currentPage - 1
onAppear()
@ -177,7 +177,7 @@ struct LibraryView: View {
Image(systemName: "chevron.left")
}
}
if(currentPage < totalPages - 1) {
if currentPage < totalPages - 1 {
Button {
currentPage = currentPage + 1
onAppear()
@ -185,7 +185,7 @@ struct LibraryView: View {
Image(systemName: "chevron.right")
}
}
if(usingParentID != "") {
if usingParentID != "" {
NavigationLink(destination: LibrarySearchView(usingParentID: usingParentID)) {
Image(systemName: "magnifyingglass")
}
@ -195,4 +195,4 @@ struct LibraryView: View {
}
}
//stream BM^S by nicki!
// stream BM^S by nicki!

View File

@ -14,7 +14,7 @@ struct LoadingView<Content>: View where Content: View {
var text: String? // the text to display under the ProgressView - defaults to "Loading..."
var body: some View {
GeometryReader { geometry in
GeometryReader { _ in
ZStack(alignment: .center) {
// the content to display - if the modal is showing, we'll blur it
content()
@ -30,7 +30,7 @@ struct LoadingView<Content>: View where Content: View {
// the magic bit - our ProgressView just displays an activity
// indicator, with some text underneath showing what we are doing
HStack() {
HStack {
ProgressView()
Text(text ?? "Loading").fontWeight(.semibold).font(.callout).offset(x: 60)
Spacer()
@ -53,7 +53,7 @@ struct LoadingViewNoBlur<Content>: View where Content: View {
var text: String? // the text to display under the ProgressView - defaults to "Loading..."
var body: some View {
GeometryReader { geometry in
GeometryReader { _ in
ZStack(alignment: .center) {
// the content to display - if the modal is showing, we'll blur it
content()
@ -68,7 +68,7 @@ struct LoadingViewNoBlur<Content>: View where Content: View {
// the magic bit - our ProgressView just displays an activity
// indicator, with some text underneath showing what we are doing
HStack() {
HStack {
ProgressView()
Text(text ?? "Loading").fontWeight(.semibold).font(.callout).offset(x: 60)
Spacer()
@ -83,4 +83,3 @@ struct LoadingViewNoBlur<Content>: View where Content: View {
}
}
}

View File

@ -200,7 +200,7 @@ struct MovieItemView: View {
HStack {
Spacer().frame(width: 16)
ForEach(item.people!, id: \.self) { person in
if(person.type! == "Actor") {
if person.type! == "Actor" {
NavigationLink(destination: LazyView {
LibraryView(withPerson: person)
}) {
@ -399,7 +399,7 @@ struct MovieItemView: View {
HStack {
Spacer().frame(width: 16)
ForEach(item.people!, id: \.self) { person in
if(person.type! == "Actor") {
if person.type! == "Actor" {
NavigationLink(destination: LazyView {
LibraryView(withPerson: person)
}) {

View File

@ -13,16 +13,16 @@ struct NextUpView: View {
@EnvironmentObject var globalData: GlobalData
@State private var items: [BaseItemDto] = []
@State private var viewDidLoad: Bool = false;
@State private var viewDidLoad: Bool = false
func onAppear() {
if(viewDidLoad == true) {
if viewDidLoad == true {
return
}
viewDidLoad = true;
viewDidLoad = true
DispatchQueue.global(qos: .userInitiated).async {
TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people])
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
@ -34,14 +34,14 @@ struct NextUpView: View {
var body: some View {
VStack(alignment: .leading) {
if(items.count != 0) {
if items.count != 0 {
Text("Next Up")
.font(.title2)
.fontWeight(.bold)
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack() {
Spacer().frame(width:16)
LazyHStack {
Spacer().frame(width: 16)
ForEach(items, id: \.id) { item in
NavigationLink(destination: ItemView(item: item)) {
VStack(alignment: .leading) {
@ -54,7 +54,7 @@ struct NextUpView: View {
}
.frame(width: 100, height: 150)
.cornerRadius(10)
Spacer().frame(height:5)
Spacer().frame(height: 5)
Text(item.seriesName!)
.font(.caption)
.fontWeight(.semibold)
@ -66,7 +66,7 @@ struct NextUpView: View {
.foregroundColor(.secondary)
.lineLimit(1)
}.frame(width: 100)
Spacer().frame(width:16)
Spacer().frame(width: 16)
}
}
}

View File

@ -14,7 +14,6 @@ struct PersistenceController {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
do {
try viewContext.save()
} catch {
@ -36,7 +35,7 @@ struct PersistenceController {
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores(completionHandler: { (_, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

View File

@ -24,12 +24,12 @@ struct SeasonItemView: View {
}
func onAppear() {
if(viewDidLoad) {
if viewDidLoad {
return
}
DispatchQueue.global(qos: .userInitiated).async {
TvShowsAPI.getEpisodes(seriesId: item.seriesId ?? "", userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], seasonId: item.id ?? "")
TvShowsAPI.getEpisodes(seriesId: item.seriesId ?? "", userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], seasonId: item.id ?? "")
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
isLoading = false

View File

@ -13,22 +13,22 @@ struct SeriesItemView: View {
@EnvironmentObject private var globalData: GlobalData
@EnvironmentObject private var orientationInfo: OrientationInfo
var item: BaseItemDto;
var item: BaseItemDto
@State private var seasons: [BaseItemDto] = [];
@State private var isLoading: Bool = true;
@State private var viewDidLoad: Bool = false;
@State private var seasons: [BaseItemDto] = []
@State private var isLoading: Bool = true
@State private var viewDidLoad: Bool = false
func onAppear() {
recalcTracks()
if(viewDidLoad) {
return;
if viewDidLoad {
return
}
isLoading = true
DispatchQueue.global(qos: .userInitiated).async {
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], isMissing: false)
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], isMissing: false)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
@ -40,12 +40,11 @@ struct SeriesItemView: View {
}
}
//MARK: Grid tracks
// MARK: Grid tracks
func recalcTracks() {
let trkCnt: Int = Int(floor(UIScreen.main.bounds.size.width / 125));
let trkCnt: Int = Int(floor(UIScreen.main.bounds.size.width / 125))
tracks = []
for _ in (0..<trkCnt)
{
for _ in (0..<trkCnt) {
tracks.append(GridItem.init(.flexible()))
}
}
@ -66,7 +65,7 @@ struct SeriesItemView: View {
.frame(width: 100, height: 150)
.cornerRadius(10)
}
.frame(width:100, height: 150)
.frame(width: 100, height: 150)
.cornerRadius(10)
.shadow(radius: 5)
Text(season.name ?? "")
@ -74,7 +73,7 @@ struct SeriesItemView: View {
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
if(season.productionYear != nil) {
if season.productionYear != nil {
Text(String(season.productionYear!))
.foregroundColor(.secondary)
.font(.caption)
@ -84,7 +83,7 @@ struct SeriesItemView: View {
}
}
Spacer().frame(height: 2)
}.onChange(of: orientationInfo.orientation) { ip in
}.onChange(of: orientationInfo.orientation) { _ in
recalcTracks()
}
}

View File

@ -11,21 +11,21 @@ import JellyfinAPI
import MediaPlayer
struct Subtitle {
var name: String;
var id: Int32;
var url: URL;
var delivery: SubtitleDeliveryMethod;
var codec: String;
var name: String
var id: Int32
var url: URL
var delivery: SubtitleDeliveryMethod
var codec: String
}
struct AudioTrack {
var name: String;
var id: Int32;
var name: String
var id: Int32
}
class PlaybackItem: ObservableObject {
@Published var videoType: PlayMethod = .directPlay;
@Published var videoUrl: URL = URL(string: "https://example.com")!;
@Published var videoType: PlayMethod = .directPlay
@Published var videoUrl: URL = URL(string: "https://example.com")!
}
protocol PlayerViewControllerDelegate: AnyObject {
@ -50,34 +50,34 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
@IBOutlet weak var jumpForwardButton: UIButton!
@IBOutlet weak var playerSettingsButton: UIButton!
var shouldShowLoadingScreen: Bool = false;
var ssTargetValueOffset: Int = 0;
var ssStartValue: Int = 0;
var optionsVC: VideoPlayerSettingsView?;
var shouldShowLoadingScreen: Bool = false
var ssTargetValueOffset: Int = 0
var ssStartValue: Int = 0
var optionsVC: VideoPlayerSettingsView?
var paused: Bool = true;
var lastTime: Float = 0.0;
var startTime: Int = 0;
var controlsAppearTime: Double = 0;
var paused: Bool = true
var lastTime: Float = 0.0
var startTime: Int = 0
var controlsAppearTime: Double = 0
var selectedAudioTrack: Int32 = -1 {
didSet {
print(selectedAudioTrack)
}
};
}
var selectedCaptionTrack: Int32 = -1 {
didSet {
print(selectedCaptionTrack)
}
}
var playSessionId: String = "";
var lastProgressReportTime: Double = 0;
var playSessionId: String = ""
var lastProgressReportTime: Double = 0
var subtitleTrackArray: [Subtitle] = [];
var audioTrackArray: [AudioTrack] = [];
var subtitleTrackArray: [Subtitle] = []
var audioTrackArray: [AudioTrack] = []
var manifest: BaseItemDto = BaseItemDto();
var playbackItem = PlaybackItem();
var manifest: BaseItemDto = BaseItemDto()
var playbackItem = PlaybackItem()
@IBAction func seekSliderStart(_ sender: Any) {
sendProgressReport(eventName: "pause")
@ -86,16 +86,16 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
@IBAction func seekSliderValueChanged(_ sender: Any) {
let videoDuration = Double(mediaPlayer.time.intValue + abs(mediaPlayer.remainingTime.intValue))/1000
let secondsScrubbedTo = round(Double(seekSlider.value) * videoDuration);
let scrubRemaining = videoDuration - secondsScrubbedTo;
let remainingTime = scrubRemaining;
let hours = floor(remainingTime / 3600);
let minutes = (remainingTime.truncatingRemainder(dividingBy: 3600)) / 60;
let seconds = (remainingTime.truncatingRemainder(dividingBy: 3600)).truncatingRemainder(dividingBy: 60);
if(hours != 0) {
timeText.text = "\(Int(hours)):\(String(Int(floor(minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int(floor(seconds))).leftPad(toWidth: 2, withString: "0"))";
let secondsScrubbedTo = round(Double(seekSlider.value) * videoDuration)
let scrubRemaining = videoDuration - secondsScrubbedTo
let remainingTime = scrubRemaining
let hours = floor(remainingTime / 3600)
let minutes = (remainingTime.truncatingRemainder(dividingBy: 3600)) / 60
let seconds = (remainingTime.truncatingRemainder(dividingBy: 3600)).truncatingRemainder(dividingBy: 60)
if hours != 0 {
timeText.text = "\(Int(hours)):\(String(Int(floor(minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int(floor(seconds))).leftPad(toWidth: 2, withString: "0"))"
} else {
timeText.text = "\(String(Int(floor(minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int(floor(seconds))).leftPad(toWidth: 2, withString: "0"))";
timeText.text = "\(String(Int(floor(minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int(floor(seconds))).leftPad(toWidth: 2, withString: "0"))"
}
}
@ -103,14 +103,14 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
print("ss end")
let videoPosition = Double(mediaPlayer.time.intValue)
let videoDuration = Double(mediaPlayer.time.intValue + abs(mediaPlayer.remainingTime.intValue))
//Scrub is value from 0..1 - find position in video and add / or remove.
let secondsScrubbedTo = round(Double(seekSlider.value) * videoDuration);
let offset = secondsScrubbedTo - videoPosition;
// Scrub is value from 0..1 - find position in video and add / or remove.
let secondsScrubbedTo = round(Double(seekSlider.value) * videoDuration)
let offset = secondsScrubbedTo - videoPosition
mediaPlayer.play()
if(offset > 0) {
mediaPlayer.jumpForward(Int32(offset)/1000);
if offset > 0 {
mediaPlayer.jumpForward(Int32(offset)/1000)
} else {
mediaPlayer.jumpBackward(Int32(abs(offset))/1000);
mediaPlayer.jumpBackward(Int32(abs(offset))/1000)
}
sendProgressReport(eventName: "unpause")
}
@ -131,13 +131,13 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
}
@IBAction func jumpBackTapped(_ sender: Any) {
if(paused == false) {
if paused == false {
mediaPlayer.jumpBackward(15)
}
}
@IBAction func jumpForwardTapped(_ sender: Any) {
if(paused == false) {
if paused == false {
mediaPlayer.jumpForward(30)
}
}
@ -145,14 +145,14 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
@IBOutlet weak var mainActionButton: UIButton!
@IBAction func mainActionButtonPressed(_ sender: Any) {
print(mediaPlayer.state.rawValue)
if(paused) {
if paused {
mediaPlayer.play()
mainActionButton.setImage(UIImage(systemName: "pause"), for: .normal)
paused = false;
paused = false
} else {
mediaPlayer.pause()
mainActionButton.setImage(UIImage(systemName: "play"), for: .normal)
paused = true;
paused = true
}
}
@ -163,7 +163,6 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
optionsVC?.modalPresentationStyle = .popover
optionsVC?.popoverPresentationController?.sourceView = playerSettingsButton
// Present the view controller (in a popover).
self.present(optionsVC!, animated: true) {
print("popover visible, pause playback")
@ -188,41 +187,41 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
func setupNowPlayingCC() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true;
commandCenter.pauseCommand.isEnabled = true;
commandCenter.seekForwardCommand.isEnabled = true;
commandCenter.seekBackwardCommand.isEnabled = true;
commandCenter.playCommand.isEnabled = true
commandCenter.pauseCommand.isEnabled = true
commandCenter.seekForwardCommand.isEnabled = true
commandCenter.seekBackwardCommand.isEnabled = true
commandCenter.changePlaybackPositionCommand.isEnabled = true
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget{ event in
commandCenter.pauseCommand.addTarget { _ in
self.mediaPlayer.pause()
self.sendProgressReport(eventName: "pause")
return .success
}
//Add handler for Play command
commandCenter.playCommand.addTarget{ event in
// Add handler for Play command
commandCenter.playCommand.addTarget { _ in
self.mediaPlayer.play()
self.sendProgressReport(eventName: "unpause")
return .success
}
//Add handler for FF command
commandCenter.seekForwardCommand.addTarget{ event in
// Add handler for FF command
commandCenter.seekForwardCommand.addTarget { _ in
self.mediaPlayer.jumpForward(30)
self.sendProgressReport(eventName: "timeupdate")
return .success
}
//Add handler for RW command
commandCenter.seekBackwardCommand.addTarget{ event in
// Add handler for RW command
commandCenter.seekBackwardCommand.addTarget { _ in
self.mediaPlayer.jumpBackward(15)
self.sendProgressReport(eventName: "timeupdate")
return .success
}
//Scrubber
// Scrubber
commandCenter.changePlaybackPositionCommand.addTarget { [weak self](remoteEvent) -> MPRemoteCommandHandlerStatus in
guard let self = self else {return .commandFailed}
@ -230,11 +229,11 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
let targetSeconds = event.positionTime
let videoPosition = Double(self.mediaPlayer.time.intValue)
let offset = targetSeconds - videoPosition;
if(offset > 0) {
self.mediaPlayer.jumpForward(Int32(offset)/1000);
let offset = targetSeconds - videoPosition
if offset > 0 {
self.mediaPlayer.jumpForward(Int32(offset)/1000)
} else {
self.mediaPlayer.jumpBackward(Int32(abs(offset))/1000);
self.mediaPlayer.jumpBackward(Int32(abs(offset))/1000)
}
self.sendProgressReport(eventName: "unpause")
@ -244,7 +243,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
}
}
var nowPlayingInfo = [String : Any]()
var nowPlayingInfo = [String: Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = manifest.name ?? ""
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
@ -258,29 +257,28 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
override func viewDidLoad() {
super.viewDidLoad()
//View has loaded.
// View has loaded.
//Rotate to landscape only if necessary
UIViewController.attemptRotationToDeviceOrientation();
// Rotate to landscape only if necessary
UIViewController.attemptRotationToDeviceOrientation()
mediaPlayer.perform(Selector(("setTextRendererFontSize:")), with: 14)
//mediaPlayer.wrappedValue.perform(Selector(("setTextRendererFont:")), with: "Copperplate")
// mediaPlayer.wrappedValue.perform(Selector(("setTextRendererFont:")), with: "Copperplate")
mediaPlayer.delegate = self
mediaPlayer.drawable = videoContentView
if(manifest.type == "Movie") {
if manifest.type == "Movie" {
titleLabel.text = manifest.name
} else {
titleLabel.text = "S\(String(manifest.parentIndexNumber!)):E\(String(manifest.indexNumber!))\(manifest.name!)"
}
//Fetch max bitrate from UserDefaults depending on current connection mode
// Fetch max bitrate from UserDefaults depending on current connection mode
let defaults = UserDefaults.standard
let maxBitrate = globalData.isInNetwork ? defaults.integer(forKey: "InNetworkBandwidth") : defaults.integer(forKey: "OutOfNetworkBandwidth")
//Build a device profile
// Build a device profile
let builder = DeviceProfileBuilder()
builder.setMaxBitrate(bitrate: maxBitrate)
let profile = builder.buildProfile()
@ -294,77 +292,77 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
}, receiveValue: { [self] response in
playSessionId = response.playSessionId!
let mediaSource = response.mediaSources!.first.self!
if(mediaSource.transcodingUrl != nil) {
//Item is being transcoded by request of server
if mediaSource.transcodingUrl != nil {
// Item is being transcoded by request of server
let streamURL = URL(string: "\(globalData.server.baseURI!)\(mediaSource.transcodingUrl!)")
let item = PlaybackItem()
item.videoType = .transcode
item.videoUrl = streamURL!
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "")
subtitleTrackArray.append(disableSubtitleTrack);
subtitleTrackArray.append(disableSubtitleTrack)
//Loop through media streams and add to array
// Loop through media streams and add to array
for stream in mediaSource.mediaStreams! {
if(stream.type == .subtitle) {
if stream.type == .subtitle {
let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!)
subtitleTrackArray.append(subtitle);
subtitleTrackArray.append(subtitle)
}
if(stream.type == .audio) {
if stream.type == .audio {
let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!))
if(stream.isDefault! == true) {
selectedAudioTrack = Int32(stream.index!);
if stream.isDefault! == true {
selectedAudioTrack = Int32(stream.index!)
}
audioTrackArray.append(subtitle);
audioTrackArray.append(subtitle)
}
}
if(selectedAudioTrack == -1) {
if(audioTrackArray.count > 0) {
selectedAudioTrack = audioTrackArray[0].id;
if selectedAudioTrack == -1 {
if audioTrackArray.count > 0 {
selectedAudioTrack = audioTrackArray[0].id
}
}
self.sendPlayReport()
playbackItem = item;
playbackItem = item
} else {
//Item will be directly played by the client.
let streamURL: URL = URL(string: "\(globalData.server.baseURI!)/Videos/\(manifest.id!)/stream?Static=true&mediaSourceId=\(manifest.id!)&deviceId=\(globalData.user.device_uuid!)&api_key=\(globalData.authToken)&Tag=\(mediaSource.eTag!)")!;
// Item will be directly played by the client.
let streamURL: URL = URL(string: "\(globalData.server.baseURI!)/Videos/\(manifest.id!)/stream?Static=true&mediaSourceId=\(manifest.id!)&deviceId=\(globalData.user.device_uuid!)&api_key=\(globalData.authToken)&Tag=\(mediaSource.eTag!)")!
let item = PlaybackItem()
item.videoUrl = streamURL
item.videoType = .directPlay
let disableSubtitleTrack = Subtitle(name: "Disabled", id: -1, url: URL(string: "https://example.com")!, delivery: .embed, codec: "")
subtitleTrackArray.append(disableSubtitleTrack);
subtitleTrackArray.append(disableSubtitleTrack)
//Loop through media streams and add to array
// Loop through media streams and add to array
for stream in mediaSource.mediaStreams! {
if(stream.type == .subtitle) {
if stream.type == .subtitle {
let deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")!
let subtitle = Subtitle(name: stream.displayTitle!, id: Int32(stream.index!), url: deliveryUrl, delivery: stream.deliveryMethod!, codec: stream.codec!)
subtitleTrackArray.append(subtitle);
subtitleTrackArray.append(subtitle)
}
if(stream.type == .audio) {
if stream.type == .audio {
let subtitle = AudioTrack(name: stream.displayTitle!, id: Int32(stream.index!))
if(stream.isDefault! == true) {
selectedAudioTrack = Int32(stream.index!);
if stream.isDefault! == true {
selectedAudioTrack = Int32(stream.index!)
}
audioTrackArray.append(subtitle);
audioTrackArray.append(subtitle)
}
}
if(selectedAudioTrack == -1) {
if(audioTrackArray.count > 0) {
selectedAudioTrack = audioTrackArray[0].id;
if selectedAudioTrack == -1 {
if audioTrackArray.count > 0 {
selectedAudioTrack = audioTrackArray[0].id
}
}
self.sendPlayReport()
playbackItem = item;
playbackItem = item
}
self.setupNowPlayingCC()
@ -374,15 +372,15 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
print(manifest.userData?.playbackPositionTicks ?? 0)
mediaPlayer.jumpForward(Int32(manifest.userData?.playbackPositionTicks ?? 0/10000000))
mediaPlayer.pause()
subtitleTrackArray.forEach() { sub in
if(sub.id != -1 && sub.delivery == .external && sub.codec != "subrip") {
subtitleTrackArray.forEach { sub in
if sub.id != -1 && sub.delivery == .external && sub.codec != "subrip" {
print("adding subs for id: \(sub.id) w/ url: \(sub.url)")
mediaPlayer.addPlaybackSlave(sub.url, type: .subtitle, enforce: false)
}
}
delegate?.showLoadingView(self)
while(mediaPlayer.numberOfSubtitlesTracks != subtitleTrackArray.count - 1) {}
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack;
while mediaPlayer.numberOfSubtitlesTracks != subtitleTrackArray.count - 1 {}
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
mediaPlayer.pause()
mediaPlayer.play()
})
@ -392,10 +390,10 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = true;
self.tabBarController?.tabBar.isHidden = true
}
//MARK: VideoPlayerSettings Delegate
// MARK: VideoPlayerSettings Delegate
func subtitleTrackChanged(newTrackID: Int32) {
selectedCaptionTrack = newTrackID
mediaPlayer.currentVideoSubTitleIndex = newTrackID
@ -406,24 +404,23 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
mediaPlayer.currentAudioTrackIndex = newTrackID
}
//MARK: VLCMediaPlayer Delegates
// MARK: VLCMediaPlayer Delegates
func mediaPlayerStateChanged(_ aNotification: Notification!) {
let currentState: VLCMediaPlayerState = mediaPlayer.state
switch currentState {
case .stopped :
break;
break
case .ended :
break;
break
case .playing :
print("Video is playing")
sendProgressReport(eventName: "unpause")
delegate?.hideLoadingView(self)
paused = false;
paused = false
case .paused :
print("Video is paused)")
paused = true;
paused = true
case .opening :
print("Video is opening)")
@ -446,53 +443,53 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
}
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
let time = mediaPlayer.position;
if(time != lastTime) {
paused = false;
let time = mediaPlayer.position
if time != lastTime {
paused = false
mainActionButton.setImage(UIImage(systemName: "pause"), for: .normal)
seekSlider.setValue(mediaPlayer.position, animated: true)
delegate?.hideLoadingView(self)
let remainingTime = abs(mediaPlayer.remainingTime.intValue)/1000;
let hours = remainingTime / 3600;
let minutes = (remainingTime % 3600) / 60;
let seconds = (remainingTime % 3600) % 60;
var timeTextStr = "";
if(hours != 0) {
timeTextStr = "\(Int(hours)):\(String(Int((minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int((seconds))).leftPad(toWidth: 2, withString: "0"))";
let remainingTime = abs(mediaPlayer.remainingTime.intValue)/1000
let hours = remainingTime / 3600
let minutes = (remainingTime % 3600) / 60
let seconds = (remainingTime % 3600) % 60
var timeTextStr = ""
if hours != 0 {
timeTextStr = "\(Int(hours)):\(String(Int((minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int((seconds))).leftPad(toWidth: 2, withString: "0"))"
} else {
timeTextStr = "\(String(Int((minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int((seconds))).leftPad(toWidth: 2, withString: "0"))";
timeTextStr = "\(String(Int((minutes))).leftPad(toWidth: 2, withString: "0")):\(String(Int((seconds))).leftPad(toWidth: 2, withString: "0"))"
}
timeText.text = timeTextStr
if(CACurrentMediaTime() - controlsAppearTime > 5) {
if CACurrentMediaTime() - controlsAppearTime > 5 {
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
self.videoControlsView.alpha = 0.0
}, completion: { (finished: Bool) in
self.videoControlsView.isHidden = true;
}, completion: { (_: Bool) in
self.videoControlsView.isHidden = true
self.videoControlsView.alpha = 1
})
controlsAppearTime = 10000000000000000000000;
controlsAppearTime = 10000000000000000000000
}
} else {
paused = true;
paused = true
}
lastTime = time;
lastTime = time
if(CACurrentMediaTime() - lastProgressReportTime > 5) {
if CACurrentMediaTime() - lastProgressReportTime > 5 {
sendProgressReport(eventName: "timeupdate")
lastProgressReportTime = CACurrentMediaTime()
}
}
//MARK: Jellyfin Playstate updates
// MARK: Jellyfin Playstate updates
func sendProgressReport(eventName: String) {
let progressInfo = PlaybackProgressInfo(canSeek: true, item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, audioStreamIndex: Int(selectedAudioTrack), subtitleStreamIndex: Int(selectedCaptionTrack), isPaused: (mediaPlayer.state == .paused), isMuted: false, positionTicks: Int64(mediaPlayer.position * Float(manifest.runTimeTicks!)), playbackStartTimeTicks: Int64(startTime), volumeLevel: 100, brightness: 100, aspectRatio: nil, playMethod: playbackItem.videoType, liveStreamId: nil, playSessionId: playSessionId, repeatMode: .repeatNone, nowPlayingQueue: [], playlistItemId: "playlistItem0")
PlaystateAPI.reportPlaybackProgress(playbackProgressInfo: progressInfo)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
}, receiveValue: { response in
}, receiveValue: { _ in
print("Playback progress report sent!")
})
.store(in: &globalData.pendingAPIRequests)
@ -504,7 +501,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
PlaystateAPI.reportPlaybackStopped(playbackStopInfo: stopInfo)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
}, receiveValue: { response in
}, receiveValue: { _ in
print("Playback stop report sent!")
})
.store(in: &globalData.pendingAPIRequests)
@ -518,7 +515,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
PlaystateAPI.reportPlaybackStart(playbackStartInfo: startInfo)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: self.globalData, completion: completion)
}, receiveValue: { response in
}, receiveValue: { _ in
print("Playback start report sent!")
})
.store(in: &globalData.pendingAPIRequests)
@ -528,7 +525,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
struct VLCPlayerWithControls: UIViewControllerRepresentable {
var item: BaseItemDto
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject private var globalData: GlobalData;
@EnvironmentObject private var globalData: GlobalData
var loadBinding: Binding<Bool>
var pBinding: Binding<Bool>
@ -543,15 +540,15 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
}
func hideLoadingView(_ viewController: PlayerViewController) {
self.loadBinding.wrappedValue = false;
self.loadBinding.wrappedValue = false
}
func showLoadingView(_ viewController: PlayerViewController) {
self.loadBinding.wrappedValue = true;
self.loadBinding.wrappedValue = true
}
func exitPlayer(_ viewController: PlayerViewController) {
self.pBinding.wrappedValue = false;
self.pBinding.wrappedValue = false
}
}
@ -559,14 +556,13 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
Coordinator(loadBinding: self.loadBinding, pBinding: self.pBinding)
}
typealias UIViewControllerType = PlayerViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<VLCPlayerWithControls>) -> VLCPlayerWithControls.UIViewControllerType {
let storyboard = UIStoryboard(name: "VideoPlayer", bundle: nil)
let customViewController = storyboard.instantiateViewController(withIdentifier: "VideoPlayer") as! PlayerViewController
customViewController.manifest = item;
customViewController.delegate = context.coordinator;
customViewController.globalData = globalData;
customViewController.manifest = item
customViewController.delegate = context.coordinator
customViewController.globalData = globalData
return customViewController
}

View File

@ -30,17 +30,17 @@ class VideoPlayerSettingsView: UIViewController {
}
struct VideoPlayerSettings: View {
@State var delegate: PlayerViewController!
@State var captionTrack: Int32 = -99;
@State var audioTrack: Int32 = -99;
@State weak var delegate: PlayerViewController!
@State var captionTrack: Int32 = -99
@State var audioTrack: Int32 = -99
init(delegate: PlayerViewController) {
self.delegate = delegate
}
var body: some View {
NavigationView() {
Form() {
NavigationView {
Form {
Picker("Closed Captions", selection: $captionTrack) {
ForEach(delegate.subtitleTrackArray, id: \.id) { caption in
Text(caption.name).tag(caption.id)
@ -60,11 +60,11 @@ struct VideoPlayerSettings: View {
.navigationTitle("Audio & Captions")
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
if(UIDevice.current.userInterfaceIdiom == .phone) {
if UIDevice.current.userInterfaceIdiom == .phone {
Button {
self.delegate.settingsPopoverDismissed()
} label: {
HStack() {
HStack {
Image(systemName: "chevron.left")
Text("Back").font(.callout)
}

View File

@ -9,57 +9,57 @@ import Foundation
import JellyfinAPI
import UIKit
//001fC^ = dark grey plain blurhash
// 001fC^ = dark grey plain blurhash
extension BaseItemDto {
//MARK: Images
// MARK: Images
func getSeriesBackdropImageBlurHash() -> String {
let rawImgURL = self.getSeriesBackdropImage(baseURL: "", maxWidth: 1).absoluteString;
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1];
let rawImgURL = self.getSeriesBackdropImage(baseURL: "", maxWidth: 1).absoluteString
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1]
return self.imageBlurHashes?.backdrop?[imgTag] ?? "001fC^";
return self.imageBlurHashes?.backdrop?[imgTag] ?? "001fC^"
}
func getSeriesPrimaryImageBlurHash() -> String {
let rawImgURL = self.getSeriesPrimaryImage(baseURL: "", maxWidth: 1).absoluteString;
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1];
let rawImgURL = self.getSeriesPrimaryImage(baseURL: "", maxWidth: 1).absoluteString
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1]
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^";
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^"
}
func getPrimaryImageBlurHash() -> String {
let rawImgURL = self.getPrimaryImage(baseURL: "", maxWidth: 1).absoluteString;
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1];
let rawImgURL = self.getPrimaryImage(baseURL: "", maxWidth: 1).absoluteString
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1]
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^";
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^"
}
func getBackdropImageBlurHash() -> String {
let rawImgURL = self.getBackdropImage(baseURL: "", maxWidth: 1).absoluteString;
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1];
let rawImgURL = self.getBackdropImage(baseURL: "", maxWidth: 1).absoluteString
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1]
if(rawImgURL.contains("Backdrop")) {
return self.imageBlurHashes?.backdrop?[imgTag] ?? "001fC^";
if rawImgURL.contains("Backdrop") {
return self.imageBlurHashes?.backdrop?[imgTag] ?? "001fC^"
} else {
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^";
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^"
}
}
func getBackdropImage(baseURL: String, maxWidth: Int) -> URL {
var imageType = "";
var imageTag = "";
var imageType = ""
var imageTag = ""
if(self.primaryImageAspectRatio ?? 0.0 < 1.0) {
imageType = "Backdrop";
if self.primaryImageAspectRatio ?? 0.0 < 1.0 {
imageType = "Backdrop"
imageTag = (self.backdropImageTags ?? [""])[0]
} else {
imageType = "Primary";
imageType = "Primary"
imageTag = self.imageTags?["Primary"] ?? ""
}
if(imageTag == "") {
imageType = "Backdrop";
if imageTag == "" {
imageType = "Backdrop"
imageTag = self.parentBackdropImageTags?[0] ?? ""
}
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
@ -68,8 +68,8 @@ extension BaseItemDto {
}
func getSeriesBackdropImage(baseURL: String, maxWidth: Int) -> URL {
let imageType = "Backdrop";
let imageTag = (self.parentBackdropImageTags ?? [])[0];
let imageType = "Backdrop"
let imageTag = (self.parentBackdropImageTags ?? [])[0]
print(imageType)
print(imageTag)
@ -80,7 +80,7 @@ extension BaseItemDto {
}
func getSeriesPrimaryImage(baseURL: String, maxWidth: Int) -> URL {
let imageType = "Primary";
let imageType = "Primary"
let imageTag = self.seriesPrimaryImageTag ?? ""
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
let urlString = "\(baseURL)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
@ -88,10 +88,10 @@ extension BaseItemDto {
}
func getPrimaryImage(baseURL: String, maxWidth: Int) -> URL {
let imageType = "Primary";
var imageTag = self.imageTags?["Primary"] ?? "";
let imageType = "Primary"
var imageTag = self.imageTags?["Primary"] ?? ""
if(imageTag == "") {
if imageTag == "" {
imageTag = self.seriesPrimaryImageTag ?? ""
}
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
@ -100,7 +100,7 @@ extension BaseItemDto {
return URL(string: urlString)!
}
//MARK: Calculations
// MARK: Calculations
func getItemRuntime() -> String {
let seconds: Int = Int(self.runTimeTicks!) / 10_000_000
let hours = (seconds / 3600)
@ -113,8 +113,8 @@ extension BaseItemDto {
}
func getItemProgressString() -> String {
if(self.userData?.playbackPositionTicks == nil || self.userData?.playbackPositionTicks == 0) {
return "";
if self.userData?.playbackPositionTicks == nil || self.userData?.playbackPositionTicks == 0 {
return ""
}
let remainingSecs = Int(self.runTimeTicks! - (self.userData?.playbackPositionTicks!)!) / 10_000_000
@ -130,7 +130,7 @@ extension BaseItemDto {
extension BaseItemPerson {
func getImage(baseURL: String, maxWidth: Int) -> URL {
let imageType = "Primary";
let imageType = "Primary"
let imageTag = self.primaryImageTag ?? ""
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
@ -140,9 +140,9 @@ extension BaseItemPerson {
}
func getBlurHash() -> String {
let rawImgURL = self.getImage(baseURL: "", maxWidth: 1).absoluteString;
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1];
let rawImgURL = self.getImage(baseURL: "", maxWidth: 1).absoluteString
let imgTag = rawImgURL.components(separatedBy: "&tag=")[1]
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^";
return self.imageBlurHashes?.primary?[imgTag] ?? "001fC^"
}
}

View File

@ -115,14 +115,12 @@ private func signPow(_ value: Float, _ exp: Float) -> Float {
private func linearTosRGB(_ value: Float) -> Int {
let v = max(0, min(1, value))
if v <= 0.0031308 { return Int(v * 12.92 * 255 + 0.5) }
else { return Int((1.055 * pow(v, 1 / 2.4) - 0.055) * 255 + 0.5) }
if v <= 0.0031308 { return Int(v * 12.92 * 255 + 0.5) } else { return Int((1.055 * pow(v, 1 / 2.4) - 0.055) * 255 + 0.5) }
}
private func sRGBToLinear<Type: BinaryInteger>(_ value: Type) -> Float {
let v = Float(Int64(value)) / 255
if v <= 0.04045 { return v / 12.92 }
else { return pow((v + 0.055) / 1.055, 2.4) }
if v <= 0.04045 { return v / 12.92 } else { return pow((v + 0.055) / 1.055, 2.4) }
}
private let encodeCharacters: [String] = {

View File

@ -7,7 +7,6 @@
import Foundation
public extension Collection {
/// SwifterSwift: Safe protects the array from out of bounds by use of optional.

View File

@ -15,11 +15,11 @@ func HandleAPIRequestCompletion(globalData: GlobalData, completion: Subscribers.
break
case .failure(let error):
if let err = error as? ErrorResponse {
switch(err){
switch err {
case .error(401, _, _, _):
globalData.expiredCredentials = true;
case .error(_, _, _, _):
globalData.networkError = true;
globalData.expiredCredentials = true
case .error:
globalData.networkError = true
}
}
break

View File

@ -19,8 +19,7 @@ struct ParallaxHeaderScrollView<Header: View, StaticOverlayView: View, Content:
staticOverlayView: StaticOverlayView,
overlayAlignment: Alignment = .center,
headerHeight: CGFloat,
content: @escaping () -> Content)
{
content: @escaping () -> Content) {
self.header = header
self.staticOverlayView = staticOverlayView
self.overlayAlignment = overlayAlignment

View File

@ -32,4 +32,3 @@ extension String {
return "\(padString)\(self)"
}
}

View File

@ -32,10 +32,10 @@ class GlobalData: ObservableObject {
@Published var authToken: String = ""
@Published var server: Server!
@Published var authHeader: String = ""
@Published var isInNetwork: Bool = true;
@Published var networkError: Bool = false;
@Published var expiredCredentials: Bool = false;
var pendingAPIRequests = Set<AnyCancellable>();
@Published var isInNetwork: Bool = true
@Published var networkError: Bool = false
@Published var expiredCredentials: Bool = false
var pendingAPIRequests = Set<AnyCancellable>()
}
extension GlobalData: Equatable {

View File

@ -10,12 +10,12 @@
import Foundation
struct UserSettings: Decodable {
var LocalMaxBitrate: Int;
var RemoteMaxBitrate: Int;
var AutoSelectSubtitles: Bool;
var AutoSelectSubtitlesLangcode: String;
var SubtitlePositionOffset: Int;
var SubtitleFontName: String;
var LocalMaxBitrate: Int
var RemoteMaxBitrate: Int
var AutoSelectSubtitles: Bool
var AutoSelectSubtitlesLangcode: String
var SubtitlePositionOffset: Int
var SubtitleFontName: String
}
struct Bitrates: Codable, Hashable {

View File

@ -380,7 +380,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -391,7 +391,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))
@ -406,7 +406,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -418,7 +418,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))
@ -431,7 +431,7 @@ struct NextUpWidget_Previews: PreviewProvider {
NextUpEntryView(entry: .init(date: Date(),
items: [
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -441,7 +441,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
UIImage(named: "WidgetHeaderSymbol"))
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))