Replace globalData with SessionManager, ServerEnvironment
This commit is contained in:
parent
c66fce752a
commit
8c0c51fa26
|
@ -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 = "<group>"; };
|
||||
53A089CF264DA9DA00D57806 /* MovieItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieItemView.swift; sourceTree = "<group>"; };
|
||||
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = JellyfinPlayer.entitlements; sourceTree = "<group>"; };
|
||||
53C4404D266C75C70049424C /* HandleAPIRequestCompletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandleAPIRequestCompletion.swift; sourceTree = "<group>"; };
|
||||
53D5E3DA264B460200BADDC8 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = "<group>"; };
|
||||
53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MobileVLCKit.xcframework; path = Carthage/Build/MobileVLCKit.xcframework; sourceTree = "<group>"; };
|
||||
53DE4BD1267098F300739748 /* SearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = "<group>"; };
|
||||
|
@ -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 */,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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(
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
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<AnyCancellable>()
|
||||
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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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)
|
||||
|
|
|
@ -9,7 +9,6 @@ import SwiftUI
|
|||
import JellyfinAPI
|
||||
|
||||
struct LibraryFilterView: View {
|
||||
@EnvironmentObject var globalData: GlobalData
|
||||
@Binding var filter: LibraryFilters
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
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 ?? "")
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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 ?? "")
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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<AnyCancellable>()
|
||||
|
||||
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)
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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)
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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)
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
|
||||
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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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<AnyCancellable>()
|
||||
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<Bool>
|
||||
var pBinding: Binding<Bool>
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Error>) {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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<NSFetchRequestResult>(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)
|
||||
|
|
Loading…
Reference in New Issue