From 8c0c51fa26ec800a08df795e61609ac628bd8616 Mon Sep 17 00:00:00 2001 From: PangMo5 Date: Tue, 15 Jun 2021 19:15:23 +0900 Subject: [PATCH] Replace globalData with SessionManager, ServerEnvironment --- JellyfinPlayer.xcodeproj/project.pbxproj | 6 --- JellyfinPlayer/ContentView.swift | 10 ++--- JellyfinPlayer/ContinueWatchingView.swift | 13 +++--- JellyfinPlayer/EpisodeItemView.swift | 40 +++++++++-------- JellyfinPlayer/ItemView.swift | 1 - JellyfinPlayer/LatestMediaView.swift | 12 +++--- JellyfinPlayer/LibraryFilterView.swift | 1 - JellyfinPlayer/LibrarySearchView.swift | 12 +++--- JellyfinPlayer/LibraryView.swift | 12 +++--- JellyfinPlayer/MovieItemView.swift | 42 ++++++++++-------- JellyfinPlayer/NextUpView.swift | 13 +++--- JellyfinPlayer/SeasonItemView.swift | 21 ++++----- JellyfinPlayer/SeriesItemView.swift | 9 ++-- JellyfinPlayer/SettingsView.swift | 2 +- JellyfinPlayer/VideoPlayer.swift | 43 ++++++++++--------- .../HandleAPIRequestCompletion.swift | 27 ------------ Shared/Shared/SessionManager.swift | 19 +++++--- 17 files changed, 136 insertions(+), 147 deletions(-) delete mode 100644 Shared/Extensions/HandleAPIRequestCompletion.swift diff --git a/JellyfinPlayer.xcodeproj/project.pbxproj b/JellyfinPlayer.xcodeproj/project.pbxproj index f335a46e..8f10ab6e 100644 --- a/JellyfinPlayer.xcodeproj/project.pbxproj +++ b/JellyfinPlayer.xcodeproj/project.pbxproj @@ -57,8 +57,6 @@ 53A431BF266B0FFE0016769F /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 53A431BE266B0FFE0016769F /* JellyfinAPI */; }; 53AD124D267029D60094A276 /* SeriesItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA526572F0700E7EA70 /* SeriesItemView.swift */; }; 53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA326572C1300E7EA70 /* SeasonItemView.swift */; }; - 53C4404E266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C4404D266C75C70049424C /* HandleAPIRequestCompletion.swift */; }; - 53C4404F266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C4404D266C75C70049424C /* HandleAPIRequestCompletion.swift */; }; 53DE4BD02670961400739748 /* EpisodeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA72657424A00E7EA70 /* EpisodeItemView.swift */; }; 53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DE4BD1267098F300739748 /* SearchBarView.swift */; }; 53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; }; @@ -195,7 +193,6 @@ 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 53A089CF264DA9DA00D57806 /* MovieItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieItemView.swift; sourceTree = ""; }; 53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = JellyfinPlayer.entitlements; sourceTree = ""; }; - 53C4404D266C75C70049424C /* HandleAPIRequestCompletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleAPIRequestCompletion.swift; sourceTree = ""; }; 53D5E3DA264B460200BADDC8 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MobileVLCKit.xcframework; path = Carthage/Build/MobileVLCKit.xcframework; sourceTree = ""; }; 53DE4BD1267098F300739748 /* SearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = ""; }; @@ -398,7 +395,6 @@ children = ( 5364F454266CA0DC0026ECBA /* APIExtensions.swift */, 5389277B263CC3DB0035E14B /* BlurHashDecode.swift */, - 53C4404D266C75C70049424C /* HandleAPIRequestCompletion.swift */, 621338B22660A07800A81A2A /* LazyView.swift */, 53E4E648263F725B00F67C6B /* MultiSelectorView.swift */, 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */, @@ -615,7 +611,6 @@ 535870652669D21600D05A09 /* ContentView.swift in Sources */, 62EC353526766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */, 535870A62669D8AE00D05A09 /* LazyView.swift in Sources */, - 53C4404F266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */, 5358706F2669D21700D05A09 /* JellyfinPlayer_tvOS.xcdatamodeld in Sources */, 5321753E2671DE9C005491E6 /* Typings.swift in Sources */, 535870632669D21600D05A09 /* JellyfinPlayer_tvOSApp.swift in Sources */, @@ -654,7 +649,6 @@ 531AC8BF26750DE20091C7EB /* ImageView.swift in Sources */, 62EC352F267666A5000E9F2D /* SessionManager.swift in Sources */, 535870AD2669D8DD00D05A09 /* Typings.swift in Sources */, - 53C4404E266C75C70049424C /* HandleAPIRequestCompletion.swift in Sources */, 62EC352C26766675000E9F2D /* ServerEnvironment.swift in Sources */, 6267B3DA2671138200A7371D /* ImageExtensions.swift in Sources */, 5377CBF7263B596A003A4E83 /* ContentView.swift in Sources */, diff --git a/JellyfinPlayer/ContentView.swift b/JellyfinPlayer/ContentView.swift index a3dee434..dac36471 100644 --- a/JellyfinPlayer/ContentView.swift +++ b/JellyfinPlayer/ContentView.swift @@ -79,13 +79,13 @@ struct ContentView: View { header.append("Token=\"\(globalData.authToken)\"") globalData.authHeader = header - JellyfinAPI.basePath = globalData.server.baseURI ?? "" + JellyfinAPI.basePath = ServerEnvironment.current.server.baseURI ?? "" JellyfinAPI.customHeaders = ["X-Emby-Authorization": globalData.authHeader] DispatchQueue.global(qos: .userInitiated).async { UserAPI.getCurrentUser() .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) loadState = loadState - 1 }, receiveValue: { response in libraries = response.configuration?.orderedViews ?? [] @@ -100,9 +100,9 @@ struct ContentView: View { }) .store(in: &globalData.pendingAPIRequests) - UserViewsAPI.getUserViews(userId: globalData.user.user_id ?? "") + UserViewsAPI.getUserViews(userId: SessionManager.current.userID ?? "") .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) loadState = loadState - 1 }, receiveValue: { response in response.items?.forEach({ item in @@ -146,7 +146,7 @@ struct ContentView: View { .onAppear(perform: startup) } else { if !jsi.did { - if isLoading || globalData.user == nil || globalData.user.user_id == nil { + if isLoading || globalData.user == nil || SessionManager.current.userID == nil { ProgressView() .onAppear(perform: startup) } else { diff --git a/JellyfinPlayer/ContinueWatchingView.swift b/JellyfinPlayer/ContinueWatchingView.swift index 2d75908b..5dedff11 100644 --- a/JellyfinPlayer/ContinueWatchingView.swift +++ b/JellyfinPlayer/ContinueWatchingView.swift @@ -8,6 +8,7 @@ import SwiftUI import JellyfinAPI +import Combine struct ProgressBar: Shape { func path(in rect: CGRect) -> Path { @@ -31,19 +32,19 @@ struct ProgressBar: Shape { } struct ContinueWatchingView: View { - @EnvironmentObject var globalData: GlobalData - @State private var items: [BaseItemDto] = [] func onAppear() { + var tempCancellables = Set() + 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: SessionManager.current.userID!, 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) + print(completion) }, receiveValue: { response in items = response.items ?? [] }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -56,7 +57,7 @@ struct ContinueWatchingView: View { NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { Spacer().frame(height: 10) - ImageView(src: item.getBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: 320), bh: item.getBackdropImageBlurHash()) + ImageView(src: item.getBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 320), bh: item.getBackdropImageBlurHash()) .frame(width: 320, height: 180) .cornerRadius(10) .overlay( diff --git a/JellyfinPlayer/EpisodeItemView.swift b/JellyfinPlayer/EpisodeItemView.swift index b20743ae..7f508cfc 100644 --- a/JellyfinPlayer/EpisodeItemView.swift +++ b/JellyfinPlayer/EpisodeItemView.swift @@ -7,9 +7,9 @@ import SwiftUI import JellyfinAPI +import Combine struct EpisodeItemView: View { - @EnvironmentObject private var globalData: GlobalData @EnvironmentObject private var orientationInfo: OrientationInfo @EnvironmentObject private var playbackInfo: VideoPlayerItem @@ -18,21 +18,22 @@ struct EpisodeItemView: View { @State private var settingState: Bool = true @State private var watched: Bool = false { didSet { + var tempCancellables = Set() if !settingState { if watched == true { - PlaystateAPI.markPlayedItem(userId: globalData.user.user_id!, itemId: item.id!) + PlaystateAPI.markPlayedItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } else { - PlaystateAPI.markUnplayedItem(userId: globalData.user.user_id!, itemId: item.id!) + PlaystateAPI.markUnplayedItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } } @@ -41,28 +42,29 @@ struct EpisodeItemView: View { @State private var favorite: Bool = false { didSet { + var tempCancellables = Set() if !settingState { if favorite == true { - UserLibraryAPI.markFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!) + UserLibraryAPI.markFavoriteItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } else { - UserLibraryAPI.unmarkFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!) + UserLibraryAPI.unmarkFavoriteItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } } } var portraitHeaderView: some View { - ImageView(src: item.getBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)), bh: item.getBackdropImageBlurHash()) + ImageView(src: item.getBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)), bh: item.getBackdropImageBlurHash()) .opacity(0.4) .blur(radius: 2.0) } @@ -70,7 +72,7 @@ struct EpisodeItemView: View { var portraitHeaderOverlayView: some View { VStack(alignment: .leading) { HStack(alignment: .bottom, spacing: 12) { - ImageView(src: item.getSeriesPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 120), bh: item.getSeriesPrimaryImageBlurHash()) + ImageView(src: item.getSeriesPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 120), bh: item.getSeriesPrimaryImageBlurHash()) .frame(width: 120, height: 180) .cornerRadius(10) VStack(alignment: .leading) { @@ -190,7 +192,7 @@ struct EpisodeItemView: View { LibraryView(withPerson: person) }) { VStack { - ImageView(src: person.getImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) + ImageView(src: person.getImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) .frame(width: 100, height: 100) .cornerRadius(10) Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1) @@ -229,7 +231,7 @@ struct EpisodeItemView: View { } else { GeometryReader { geometry in ZStack { - ImageView(src: item.getBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: 200), bh: item.getBackdropImageBlurHash()) + ImageView(src: item.getBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 200), bh: item.getBackdropImageBlurHash()) .opacity(0.3) .frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom) @@ -237,7 +239,7 @@ struct EpisodeItemView: View { .blur(radius: 4) HStack { VStack { - ImageView(src: item.getSeriesPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 120), bh: item.getSeriesPrimaryImageBlurHash()) + ImageView(src: item.getSeriesPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 120), bh: item.getSeriesPrimaryImageBlurHash()) .frame(width: 120, height: 180) .cornerRadius(10) Spacer().frame(height: 15) @@ -361,7 +363,7 @@ struct EpisodeItemView: View { LibraryView(withPerson: person) }) { VStack { - ImageView(src: person.getImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) + ImageView(src: person.getImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) .frame(width: 100, height: 100) .cornerRadius(10) Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1) diff --git a/JellyfinPlayer/ItemView.swift b/JellyfinPlayer/ItemView.swift index 01677170..a2fb880c 100644 --- a/JellyfinPlayer/ItemView.swift +++ b/JellyfinPlayer/ItemView.swift @@ -15,7 +15,6 @@ class VideoPlayerItem: ObservableObject { } struct ItemView: View { - @EnvironmentObject private var globalData: GlobalData private var item: BaseItemDto @StateObject private var videoPlayerItem: VideoPlayerItem = VideoPlayerItem() diff --git a/JellyfinPlayer/LatestMediaView.swift b/JellyfinPlayer/LatestMediaView.swift index d44f6de5..0bc47e5c 100644 --- a/JellyfinPlayer/LatestMediaView.swift +++ b/JellyfinPlayer/LatestMediaView.swift @@ -7,9 +7,9 @@ import SwiftUI import JellyfinAPI +import Combine struct LatestMediaView: View { - @EnvironmentObject var globalData: GlobalData @State var items: [BaseItemDto] = [] private var library_id: String = "" @@ -24,15 +24,17 @@ struct LatestMediaView: View { return } viewDidLoad = true + + var tempCancellables = Set() 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: SessionManager.current.userID!, parentId: library_id, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], enableUserData: true, limit: 12) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { response in items = response }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -45,7 +47,7 @@ struct LatestMediaView: View { NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { Spacer().frame(height: 10) - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: item.getPrimaryImageBlurHash()) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: item.getPrimaryImageBlurHash()) .frame(width: 100, height: 150) .cornerRadius(10) Spacer().frame(height: 5) diff --git a/JellyfinPlayer/LibraryFilterView.swift b/JellyfinPlayer/LibraryFilterView.swift index 66dfcd50..335a93f3 100644 --- a/JellyfinPlayer/LibraryFilterView.swift +++ b/JellyfinPlayer/LibraryFilterView.swift @@ -9,7 +9,6 @@ import SwiftUI import JellyfinAPI struct LibraryFilterView: View { - @EnvironmentObject var globalData: GlobalData @Binding var filter: LibraryFilters var body: some View { diff --git a/JellyfinPlayer/LibrarySearchView.swift b/JellyfinPlayer/LibrarySearchView.swift index 53870cf2..a808e087 100644 --- a/JellyfinPlayer/LibrarySearchView.swift +++ b/JellyfinPlayer/LibrarySearchView.swift @@ -7,9 +7,9 @@ import SwiftUI import JellyfinAPI +import Combine struct LibrarySearchView: View { - @EnvironmentObject var globalData: GlobalData @EnvironmentObject var orientationInfo: OrientationInfo @State private var items: [BaseItemDto] = [] @@ -29,16 +29,16 @@ struct LibrarySearchView: View { func requestSearch(query: String) { isLoading = true - + var tempCancellables = Set() 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: SessionManager.current.userID!, 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) + print(completion) }, receiveValue: { response in items = response.items ?? [] isLoading = false }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -68,7 +68,7 @@ struct LibrarySearchView: View { ForEach(items, id: \.id) { item in NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: item.getPrimaryImageBlurHash()) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: item.getPrimaryImageBlurHash()) .frame(width: 100, height: 150) .cornerRadius(10) Text(item.name ?? "") diff --git a/JellyfinPlayer/LibraryView.swift b/JellyfinPlayer/LibraryView.swift index 1273953a..d763fe7d 100644 --- a/JellyfinPlayer/LibraryView.swift +++ b/JellyfinPlayer/LibraryView.swift @@ -9,9 +9,9 @@ import SwiftUI import NukeUI import JellyfinAPI +import Combine struct LibraryView: View { - @EnvironmentObject var globalData: GlobalData @EnvironmentObject var orientationInfo: OrientationInfo @State private var items: [BaseItemDto] = [] @@ -67,11 +67,13 @@ struct LibraryView: View { isLoading = true items = [] + + var tempCancellables = Set() 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: SessionManager.current.userID!, 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) + print(completion) isLoading = false }, receiveValue: { response in let x = ceil(Double(response.totalRecordCount!) / 100.0) @@ -80,7 +82,7 @@ struct LibraryView: View { isLoading = false viewDidLoad = true }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -107,7 +109,7 @@ struct LibraryView: View { ForEach(items, id: \.id) { item in NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: item.getPrimaryImageBlurHash()) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: item.getPrimaryImageBlurHash()) .frame(width: 100, height: 150) .cornerRadius(10) Text(item.name ?? "") diff --git a/JellyfinPlayer/MovieItemView.swift b/JellyfinPlayer/MovieItemView.swift index e132ef36..262462bc 100644 --- a/JellyfinPlayer/MovieItemView.swift +++ b/JellyfinPlayer/MovieItemView.swift @@ -7,9 +7,9 @@ import SwiftUI import JellyfinAPI +import Combine struct MovieItemView: View { - @EnvironmentObject private var globalData: GlobalData @EnvironmentObject private var orientationInfo: OrientationInfo @EnvironmentObject private var playbackInfo: VideoPlayerItem @@ -18,21 +18,23 @@ struct MovieItemView: View { @State private var settingState: Bool = true @State private var watched: Bool = false { didSet { + var tempCancellables = Set() + if !settingState { if watched == true { - PlaystateAPI.markPlayedItem(userId: globalData.user.user_id!, itemId: item.id!) + PlaystateAPI.markPlayedItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } else { - PlaystateAPI.markUnplayedItem(userId: globalData.user.user_id!, itemId: item.id!) + PlaystateAPI.markUnplayedItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } } @@ -41,28 +43,30 @@ struct MovieItemView: View { @State private var favorite: Bool = false { didSet { + var tempCancellables = Set() + if !settingState { if favorite == true { - UserLibraryAPI.markFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!) + UserLibraryAPI.markFavoriteItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } else { - UserLibraryAPI.unmarkFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!) + UserLibraryAPI.unmarkFavoriteItem(userId: SessionManager.current.userID!, itemId: item.id!) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { _ in }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } } } var portraitHeaderView: some View { - ImageView(src: item.getBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)), bh: item.getBackdropImageBlurHash()) + ImageView(src: item.getBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)), bh: item.getBackdropImageBlurHash()) .opacity(0.4) .blur(radius: 2.0) } @@ -70,7 +74,7 @@ struct MovieItemView: View { var portraitHeaderOverlayView: some View { VStack(alignment: .leading) { HStack(alignment: .bottom, spacing: 12) { - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 120)) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 120)) .frame(width: 120, height: 180) .cornerRadius(10) VStack(alignment: .leading) { @@ -192,7 +196,7 @@ struct MovieItemView: View { LibraryView(withPerson: person) }) { VStack { - ImageView(src: person.getImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) + ImageView(src: person.getImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) .frame(width: 100, height: 100) .cornerRadius(10) Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1) @@ -231,7 +235,7 @@ struct MovieItemView: View { } else { GeometryReader { geometry in ZStack { - ImageView(src: item.getBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: 200), bh: item.getBackdropImageBlurHash()) + ImageView(src: item.getBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 200), bh: item.getBackdropImageBlurHash()) .opacity(0.3) .frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom) @@ -239,7 +243,7 @@ struct MovieItemView: View { .blur(radius: 4) HStack { VStack { - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 120), bh: item.getPrimaryImageBlurHash()) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 120), bh: item.getPrimaryImageBlurHash()) .frame(width: 120, height: 180) .cornerRadius(10) Spacer().frame(height: 15) @@ -365,7 +369,7 @@ struct MovieItemView: View { LibraryView(withPerson: person) }) { VStack { - ImageView(src: person.getImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) + ImageView(src: person.getImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: person.getBlurHash()) .frame(width: 100, height: 100) .cornerRadius(10) Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1) diff --git a/JellyfinPlayer/NextUpView.swift b/JellyfinPlayer/NextUpView.swift index 1a9b0804..f9a15df6 100644 --- a/JellyfinPlayer/NextUpView.swift +++ b/JellyfinPlayer/NextUpView.swift @@ -6,6 +6,7 @@ */ import SwiftUI +import Combine import JellyfinAPI struct NextUpView: View { @@ -19,14 +20,16 @@ struct NextUpView: View { } viewDidLoad = true + var tempCancellables = Set() + DispatchQueue.global(qos: .userInitiated).async { - 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) + TvShowsAPI.getNextUp(userId: SessionManager.current.userID!, limit: 12, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people]) + .sink(receiveCompletion: { result in + print(result) }, receiveValue: { response in items = response.items ?? [] }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -43,7 +46,7 @@ struct NextUpView: View { ForEach(items, id: \.id) { item in NavigationLink(destination: ItemView(item: item)) { VStack(alignment: .leading) { - ImageView(src: item.getSeriesPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: item.getSeriesPrimaryImageBlurHash()) + ImageView(src: item.getSeriesPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: item.getSeriesPrimaryImageBlurHash()) .frame(width: 100, height: 150) .cornerRadius(10) Spacer().frame(height: 5) diff --git a/JellyfinPlayer/SeasonItemView.swift b/JellyfinPlayer/SeasonItemView.swift index 0c6b0b80..6c14e653 100644 --- a/JellyfinPlayer/SeasonItemView.swift +++ b/JellyfinPlayer/SeasonItemView.swift @@ -6,10 +6,10 @@ */ import SwiftUI +import Combine import JellyfinAPI struct SeasonItemView: View { - @EnvironmentObject var globalData: GlobalData @EnvironmentObject var orientationInfo: OrientationInfo var item: BaseItemDto = BaseItemDto() @@ -26,17 +26,18 @@ struct SeasonItemView: View { if viewDidLoad { return } + var tempCancellables = Set() 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: SessionManager.current.userID!, fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people], seasonId: item.id ?? "") .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) isLoading = false }, receiveValue: { response in viewDidLoad = true episodes = response.items ?? [] }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -45,7 +46,7 @@ struct SeasonItemView: View { if isLoading { EmptyView() } else { - ImageView(src: item.getSeriesBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)), bh: item.getSeriesBackdropImageBlurHash()) + ImageView(src: item.getSeriesBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)), bh: item.getSeriesBackdropImageBlurHash()) .opacity(0.4) .blur(radius: 2.0) } @@ -53,7 +54,7 @@ struct SeasonItemView: View { var portraitHeaderOverlayView: some View { HStack(alignment: .bottom, spacing: 12) { - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 120), bh: item.getPrimaryImageBlurHash()) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 120), bh: item.getPrimaryImageBlurHash()) .frame(width: 120, height: 180) .cornerRadius(10) VStack(alignment: .leading) { @@ -92,7 +93,7 @@ struct SeasonItemView: View { ForEach(episodes, id: \.id) { episode in NavigationLink(destination: ItemView(item: episode)) { HStack { - ImageView(src: episode.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 150), bh: episode.getPrimaryImageBlurHash()) + ImageView(src: episode.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 150), bh: episode.getPrimaryImageBlurHash()) .shadow(radius: 5) .frame(width: 150, height: 90) .cornerRadius(10) @@ -151,7 +152,7 @@ struct SeasonItemView: View { } else { GeometryReader { geometry in ZStack { - ImageView(src: item.getSeriesBackdropImage(baseURL: globalData.server.baseURI!, maxWidth: 200), bh: item.getSeriesBackdropImageBlurHash()) + ImageView(src: item.getSeriesBackdropImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 200), bh: item.getSeriesBackdropImageBlurHash()) .opacity(0.4) .frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing, height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom) @@ -160,7 +161,7 @@ struct SeasonItemView: View { HStack { VStack(alignment: .leading) { Spacer().frame(height: 16) - ImageView(src: item.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 120), bh: item.getPrimaryImageBlurHash()) + ImageView(src: item.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 120), bh: item.getPrimaryImageBlurHash()) .frame(width: 120, height: 180) .cornerRadius(10) Spacer().frame(height: 4) @@ -185,7 +186,7 @@ struct SeasonItemView: View { ForEach(episodes, id: \.id) { episode in NavigationLink(destination: ItemView(item: episode)) { HStack { - ImageView(src: episode.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 150), bh: episode.getPrimaryImageBlurHash()) + ImageView(src: episode.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 150), bh: episode.getPrimaryImageBlurHash()) .shadow(radius: 5) .frame(width: 150, height: 90) .cornerRadius(10) diff --git a/JellyfinPlayer/SeriesItemView.swift b/JellyfinPlayer/SeriesItemView.swift index b6895105..02894684 100644 --- a/JellyfinPlayer/SeriesItemView.swift +++ b/JellyfinPlayer/SeriesItemView.swift @@ -7,6 +7,7 @@ import SwiftUI import JellyfinAPI +import Combine struct SeriesItemView: View { @EnvironmentObject private var orientationInfo: OrientationInfo @@ -24,17 +25,19 @@ struct SeriesItemView: View { } isLoading = true + + var tempCancellables = Set() DispatchQueue.global(qos: .userInitiated).async { TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people]) .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: globalData, completion: completion) + print(completion) }, receiveValue: { response in isLoading = false viewDidLoad = true seasons = response.items ?? [] }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &tempCancellables) } } @@ -59,7 +62,7 @@ struct SeriesItemView: View { ForEach(seasons, id: \.id) { season in NavigationLink(destination: ItemView(item: season)) { VStack(alignment: .leading) { - ImageView(src: season.getPrimaryImage(baseURL: globalData.server.baseURI!, maxWidth: 100), bh: season.getPrimaryImageBlurHash()) + ImageView(src: season.getPrimaryImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100), bh: season.getPrimaryImageBlurHash()) .frame(width: 100, height: 150) .cornerRadius(10) .shadow(radius: 5) diff --git a/JellyfinPlayer/SettingsView.swift b/JellyfinPlayer/SettingsView.swift index 1d65a8a8..ed94f0eb 100644 --- a/JellyfinPlayer/SettingsView.swift +++ b/JellyfinPlayer/SettingsView.swift @@ -24,7 +24,7 @@ struct SettingsView: View { func onAppear() { let defaults = UserDefaults.standard - username = globalData.user.username! + username = SessionManager.current.user.username! inNetworkStreamBitrate = defaults.integer(forKey: "InNetworkBandwidth") outOfNetworkStreamBitrate = defaults.integer(forKey: "OutOfNetworkBandwidth") autoSelectSubtitles = defaults.bool(forKey: "AutoSelectSubtitles") diff --git a/JellyfinPlayer/VideoPlayer.swift b/JellyfinPlayer/VideoPlayer.swift index 0fc42d12..ae53044c 100644 --- a/JellyfinPlayer/VideoPlayer.swift +++ b/JellyfinPlayer/VideoPlayer.swift @@ -9,6 +9,7 @@ import SwiftUI import MobileVLCKit import JellyfinAPI import MediaPlayer +import Combine struct Subtitle { var name: String @@ -38,6 +39,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe weak var delegate: PlayerViewControllerDelegate? + var cancellables = Set() var mediaPlayer = VLCMediaPlayer() @IBOutlet weak var timeText: UILabel! @@ -280,26 +282,27 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe // 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") + // globalData.isInNetwork ? defaults.integer(forKey: "InNetworkBandwidth") : defaults.integer(forKey: "OutOfNetworkBandwidth") + let maxBitrate = defaults.integer(forKey: "InNetworkBandwidth") // Build a device profile let builder = DeviceProfileBuilder() builder.setMaxBitrate(bitrate: maxBitrate) let profile = builder.buildProfile() - let playbackInfo = PlaybackInfoDto(userId: globalData.user.user_id!, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, deviceProfile: profile, autoOpenLiveStream: true) + let playbackInfo = PlaybackInfoDto(userId: SessionManager.current.userID!, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, deviceProfile: profile, autoOpenLiveStream: true) DispatchQueue.global(qos: .userInitiated).async { [self] in delegate?.showLoadingView(self) - MediaInfoAPI.getPostedPlaybackInfo(itemId: manifest.id!, userId: globalData.user.user_id!, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, playbackInfoDto: playbackInfo) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: self.globalData, completion: completion) + MediaInfoAPI.getPostedPlaybackInfo(itemId: manifest.id!, userId: SessionManager.current.userID!, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, autoOpenLiveStream: true, playbackInfoDto: playbackInfo) + .sink(receiveCompletion: { result in + print(result) }, 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 - let streamURL = URL(string: "\(globalData.server.baseURI!)\(mediaSource.transcodingUrl!)") + let streamURL = URL(string: "\(ServerEnvironment.current.server.baseURI!)\(mediaSource.transcodingUrl!)") let item = PlaybackItem() item.videoType = .transcode item.videoUrl = streamURL! @@ -312,7 +315,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe if stream.type == .subtitle { var deliveryUrl: URL? if stream.deliveryMethod == .external { - deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")! + deliveryUrl = URL(string: "\(ServerEnvironment.current.server.baseURI!)\(stream.deliveryUrl!)")! } else { deliveryUrl = nil } @@ -339,7 +342,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe 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!)")! + let streamURL: URL = URL(string: "\(ServerEnvironment.current.server.baseURI!)/Videos/\(manifest.id!)/stream?Static=true&mediaSourceId=\(manifest.id!)&deviceId=\(SessionManager.current.deviceID)&api_key=\(SessionManager.current.authToken)&Tag=\(mediaSource.eTag!)")! let item = PlaybackItem() item.videoUrl = streamURL @@ -353,7 +356,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe if stream.type == .subtitle { var deliveryUrl: URL? if stream.deliveryMethod == .external { - deliveryUrl = URL(string: "\(globalData.server.baseURI!)\(stream.deliveryUrl!)")! + deliveryUrl = URL(string: "\(ServerEnvironment.current.server.baseURI!)\(stream.deliveryUrl!)")! } else { deliveryUrl = nil } @@ -409,7 +412,7 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe mediaPlayer.pause() mediaPlayer.play() }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &cancellables) } } @@ -514,12 +517,12 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe 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) + .sink(receiveCompletion: { result in + print(result) }, receiveValue: { _ in print("Playback progress report sent!") }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &cancellables) } } @@ -527,12 +530,12 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe let stopInfo = PlaybackStopInfo(item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, positionTicks: Int64(mediaPlayer.position * Float(manifest.runTimeTicks!)), liveStreamId: nil, playSessionId: playSessionId, failed: nil, nextMediaType: nil, playlistItemId: "playlistItem0", nowPlayingQueue: []) PlaystateAPI.reportPlaybackStopped(playbackStopInfo: stopInfo) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: self.globalData, completion: completion) + .sink(receiveCompletion: { result in + print(result) }, receiveValue: { _ in print("Playback stop report sent!") }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &cancellables) } func sendPlayReport() { @@ -541,19 +544,18 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe let startInfo = PlaybackStartInfo(canSeek: true, item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, audioStreamIndex: Int(selectedAudioTrack), subtitleStreamIndex: Int(selectedCaptionTrack), isPaused: false, isMuted: false, positionTicks: manifest.userData?.playbackPositionTicks, playbackStartTimeTicks: Int64(startTime), volumeLevel: 100, brightness: 100, aspectRatio: nil, playMethod: playbackItem.videoType, liveStreamId: nil, playSessionId: playSessionId, repeatMode: .repeatNone, nowPlayingQueue: [], playlistItemId: "playlistItem0") PlaystateAPI.reportPlaybackStart(playbackStartInfo: startInfo) - .sink(receiveCompletion: { completion in - HandleAPIRequestCompletion(globalData: self.globalData, completion: completion) + .sink(receiveCompletion: { result in + print(result) }, receiveValue: { _ in print("Playback start report sent!") }) - .store(in: &globalData.pendingAPIRequests) + .store(in: &cancellables) } } struct VLCPlayerWithControls: UIViewControllerRepresentable { var item: BaseItemDto @Environment(\.presentationMode) var presentationMode - @EnvironmentObject private var globalData: GlobalData var loadBinding: Binding var pBinding: Binding @@ -590,7 +592,6 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable { let customViewController = storyboard.instantiateViewController(withIdentifier: "VideoPlayer") as! PlayerViewController customViewController.manifest = item customViewController.delegate = context.coordinator - customViewController.globalData = globalData return customViewController } diff --git a/Shared/Extensions/HandleAPIRequestCompletion.swift b/Shared/Extensions/HandleAPIRequestCompletion.swift deleted file mode 100644 index 621eabfa..00000000 --- a/Shared/Extensions/HandleAPIRequestCompletion.swift +++ /dev/null @@ -1,27 +0,0 @@ -/* JellyfinPlayer/Swiftfin is subject to the terms of the Mozilla Public - * License, v2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * Copyright 2021 Aiden Vigue & Jellyfin Contributors - */ - -import Foundation -import Combine -import JellyfinAPI - -func HandleAPIRequestCompletion(globalData: GlobalData, completion: Subscribers.Completion) { - switch completion { - case .finished: - break - case .failure(let error): - if let err = error as? ErrorResponse { - switch err { - case .error(401, _, _, _): - globalData.expiredCredentials = true - case .error: - globalData.networkError = true - } - } - break - } -} diff --git a/Shared/Shared/SessionManager.swift b/Shared/Shared/SessionManager.swift index 6ffcb7e0..47f3ad2e 100644 --- a/Shared/Shared/SessionManager.swift +++ b/Shared/Shared/SessionManager.swift @@ -18,7 +18,11 @@ final class SessionManager { static let current = SessionManager() fileprivate(set) var user: SignedInUser! fileprivate(set) var authHeader: String! - fileprivate(set) var deviceIDString: String + fileprivate(set) var authToken: String! + fileprivate(set) var deviceID: String + var userID: String? { + user.user_id + } init() { let savedUserRequest = NSFetchRequest(entityName: "SignedInUser") @@ -27,11 +31,11 @@ final class SessionManager { let keychain = KeychainSwift() keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain" - if let deviceID = keychain.get("DeviceIDString") { - self.deviceIDString = deviceID + if let deviceID = keychain.get("DeviceID") { + self.deviceID = deviceID } else { - self.deviceIDString = UUID().uuidString - keychain.set(deviceIDString, forKey: "DeviceIDString") + self.deviceID = UUID().uuidString + keychain.set(deviceID, forKey: "DeviceID") } guard let authToken = keychain.get("AccessToken_\(user?.user_id ?? "")") else { @@ -50,9 +54,10 @@ final class SessionManager { var header = "MediaBrowser " header.append("Client=\"SwiftFin\", ") header.append("Device=\"\(deviceName)\", ") - header.append("DeviceId=\"\(deviceIDString)\", ") + header.append("DeviceId=\"\(deviceID)\", ") header.append("Version=\"\(appVersion ?? "0.0.1")\", ") if let token = authToken { + self.authToken = token header.append("Token=\"\(token)\"") } @@ -66,7 +71,7 @@ final class SessionManager { return UserAPI.authenticateUserByName(authenticateUserByName: AuthenticateUserByName(username: username, pw: password)) .map { [unowned self] response -> (SignedInUser, String?) in let user = SignedInUser(context: PersistenceController.shared.container.viewContext) - user.device_uuid = deviceIDString + user.device_uuid = deviceID user.username = response.user?.name user.user_id = response.user?.id return (user, response.accessToken)