it all works

This commit is contained in:
Aiden Vigue 2021-06-09 17:04:33 -07:00
parent ea6b9a6326
commit 583f4a7509
15 changed files with 209 additions and 137 deletions

View File

@ -3,11 +3,13 @@
<plist version="1.0">
<dict>
<key>FILEHEADER</key>
<string>/* 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 &amp; Jellyfin Contributors
*/</string>
<string>
/*
* 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 &amp; Jellyfin Contributors
*/</string>
</dict>
</plist>

View File

@ -34,7 +34,7 @@ struct ConnectToServerView: View {
@State private var serverSkipped: Bool = false;
@State private var serverSkippedAlert: Bool = false;
@State private var skip_server_bool: Bool = false;
@State private var skip_server_obj: Server = Server();
@State private var skip_server_obj: Server!;
@Binding var rootIsActive: Bool
@ -297,9 +297,6 @@ struct ConnectToServerView: View {
.frame(width: 60, height: 60)
.cornerRadius(30.0)
.shadow(radius: 6)
.onAppear(perform: {
print("\(uri)/Users/\(publicUser.id!)/Images/Primary?width=200&quality=80&tag=\(publicUser.primaryImageTag!)")
})
} else {
Image(systemName: "person.fill")
.foregroundColor(Color(red: 1, green: 1, blue: 1).opacity(0.8))

View File

@ -37,7 +37,7 @@ struct ContinueWatchingView: View {
@State private var items: [BaseItemDto] = []
func onAppear() {
ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary,.backdrop,.thumb])
ItemsAPI.getResumeItems(userId: globalData.user.user_id ?? "", limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], mediaTypes: ["Video"], imageTypeLimit: 1, enableImageTypes: [.primary,.backdrop,.thumb])
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in

View File

@ -16,22 +16,25 @@ struct EpisodeItemView: View {
var item: BaseItemDto
@State private var settingState: Bool = true
@State private var watched: Bool = false {
didSet {
if watched == true {
PlaystateAPI.markPlayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
PlaystateAPI.markUnplayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
if !settingState {
if watched == true {
PlaystateAPI.markPlayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
PlaystateAPI.markUnplayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
}
}
}
}
@ -39,20 +42,22 @@ struct EpisodeItemView: View {
@State
private var favorite: Bool = false {
didSet {
if favorite == true {
UserLibraryAPI.markFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
UserLibraryAPI.unmarkFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
if !settingState {
if favorite == true {
UserLibraryAPI.markFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
UserLibraryAPI.unmarkFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
}
}
}
}
@ -99,7 +104,7 @@ struct EpisodeItemView: View {
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if item.officialRating != "" {
if item.officialRating != nil {
Text(item.officialRating!).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
@ -454,8 +459,9 @@ struct EpisodeItemView: View {
}
}
.onAppear(perform: {
favorite = item.userData?.isFavorite ?? false
watched = item.userData?.played ?? false
favorite = item.userData!.isFavorite!
watched = item.userData!.played!
settingState = false
})
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("\(item.seriesName ?? "") - S\(String(item.parentIndexNumber ?? 0)):E\(String(item.indexNumber ?? 0))")

View File

@ -2,6 +2,14 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSBonjourServices</key>
<array>
<string>_googlecast._tcp</string>
<string>_F007D354._googlecast._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>${PRODUCT_NAME} uses the local network to connect to your Jellyfin server &amp; discover Cast-enabled devices on your WiFi
network.</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>

View File

@ -9,8 +9,6 @@ import SwiftUI
import Introspect
import JellyfinAPI
//good lord the environmental modifiers ;P
class VideoPlayerItem: ObservableObject {
@Published var shouldShowPlayer: Bool = false;
@Published var itemToPlay: BaseItemDto = BaseItemDto();
@ -18,8 +16,6 @@ class VideoPlayerItem: ObservableObject {
struct ItemView: View {
@EnvironmentObject private var globalData: GlobalData
@State private var fullItem: BaseItemDto = BaseItemDto();
private var item: BaseItemDto;
@StateObject private var videoPlayerItem: VideoPlayerItem = VideoPlayerItem()
@ -31,23 +27,6 @@ struct ItemView: View {
self.item = item
}
func onAppear() {
if(viewDidLoad) {
return
}
isLoading = true;
UserLibraryAPI.getItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
isLoading = false
viewDidLoad = true
fullItem = response
})
.store(in: &globalData.pendingAPIRequests)
}
var body: some View {
VStack {
if(videoPlayerItem.shouldShowPlayer) {
@ -62,37 +41,32 @@ struct ItemView: View {
.overrideViewPreference(.unspecified)
.supportedOrientations(.landscape)
} else {
if(isLoading) {
ProgressView()
} else {
VStack {
if(fullItem.type == "Movie") {
MovieItemView(item: fullItem)
} else if(fullItem.type == "Season") {
SeasonItemView(item: fullItem)
} else if(fullItem.type == "Series") {
SeriesItemView(item: fullItem)
} else if(fullItem.type == "Episode") {
EpisodeItemView(item: fullItem)
} else {
Text("Type: \(fullItem.type ?? "") not implemented yet :(")
}
VStack {
if(item.type == "Movie") {
MovieItemView(item: item)
} else if(item.type == "Season") {
SeasonItemView(item: item)
} else if(item.type == "Series") {
SeriesItemView(item: item)
} else if(item.type == "Episode") {
EpisodeItemView(item: item)
} else {
Text("Type: \(item.type ?? "") not implemented yet :(")
}
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = false
}
.navigationBarHidden(false)
.navigationBarBackButtonHidden(false)
.statusBar(hidden: false)
.prefersHomeIndicatorAutoHidden(false)
.preferredColorScheme(.none)
.edgesIgnoringSafeArea([])
.overrideViewPreference(.unspecified)
.supportedOrientations(.allButUpsideDown)
.environmentObject(videoPlayerItem)
}
.introspectTabBarController { (UITabBarController) in
UITabBarController.tabBar.isHidden = false
}
.navigationBarHidden(false)
.navigationBarBackButtonHidden(false)
.statusBar(hidden: false)
.prefersHomeIndicatorAutoHidden(false)
.preferredColorScheme(.none)
.edgesIgnoringSafeArea([])
.overrideViewPreference(.unspecified)
.supportedOrientations(.allButUpsideDown)
.environmentObject(videoPlayerItem)
}
}
.onAppear(perform: onAppear)
}
}

View File

@ -26,7 +26,7 @@ struct LatestMediaView: View {
}
viewDidLoad = true;
UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage], enableUserData: true, limit: 12)
UserLibraryAPI.getLatestMedia(userId: globalData.user.user_id!, parentId: library_id, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], enableUserData: true, limit: 12)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in

View File

@ -31,7 +31,7 @@ struct LibrarySearchView: View {
func requestSearch(query: String) {
isLoading = true
print(usingParentID)
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.parentId,.primaryImageAspectRatio,.basicSyncInfo], includeItemTypes: ["Movie","Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, limit: 60, recursive: true, searchTerm: query, sortOrder: [.ascending], parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], sortBy: ["SortName"], enableUserData: true, enableImages: true)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in
@ -110,3 +110,5 @@ struct LibrarySearchView: View {
}
}
}
//stream NM5 by nicki!

View File

@ -61,7 +61,7 @@ struct LibraryView: View {
isLoading = true
items = []
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: (usingParentID != "" ? usingParentID : nil), fields: [.parentId,.primaryImageAspectRatio,.basicSyncInfo], includeItemTypes: ["Movie","Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: (personId == "" ? nil : [personId]), studioIds: (studio == "" ? nil : [studio]), genreIds: (genre == "" ? nil : [genre]), enableImages: true)
ItemsAPI.getItemsByUserId(userId: globalData.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: true, searchTerm: nil, sortOrder: filters.sortOrder, parentId: (usingParentID != "" ? usingParentID : nil), fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], includeItemTypes: ["Movie","Series"], filters: filters.filters, sortBy: filters.sortBy, enableUserData: true, personIds: (personId == "" ? nil : [personId]), studioIds: (studio == "" ? nil : [studio]), genreIds: (genre == "" ? nil : [genre]), enableImages: true)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
isLoading = false
@ -184,3 +184,5 @@ struct LibraryView: View {
}
}
}
//stream BM^S by nicki!

View File

@ -15,23 +15,26 @@ struct MovieItemView: View {
@EnvironmentObject private var playbackInfo: VideoPlayerItem
var item: BaseItemDto
@State private var settingState: Bool = true
@State private var watched: Bool = false {
didSet {
if watched == true {
PlaystateAPI.markPlayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
PlaystateAPI.markUnplayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
if !settingState {
if watched == true {
PlaystateAPI.markPlayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
PlaystateAPI.markUnplayedItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
}
}
}
}
@ -39,20 +42,22 @@ struct MovieItemView: View {
@State
private var favorite: Bool = false {
didSet {
if favorite == true {
UserLibraryAPI.markFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
UserLibraryAPI.unmarkFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
if !settingState {
if favorite == true {
UserLibraryAPI.markFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
} else {
UserLibraryAPI.unmarkFavoriteItem(userId: globalData.user.user_id!, itemId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { _ in
})
.store(in: &globalData.pendingAPIRequests)
}
}
}
}
@ -454,8 +459,9 @@ struct MovieItemView: View {
}
}
.onAppear(perform: {
favorite = item.userData?.isFavorite ?? false
watched = item.userData?.played ?? false
favorite = item.userData!.isFavorite!
watched = item.userData!.played!
settingState = false
})
.navigationBarTitleDisplayMode(.inline)
.navigationTitle(item.name!)

View File

@ -21,7 +21,7 @@ struct NextUpView: View {
}
viewDidLoad = true;
TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12)
TvShowsAPI.getNextUp(userId: globalData.user.user_id!, limit: 12, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in

View File

@ -14,8 +14,8 @@ struct SeasonItemView: View {
@EnvironmentObject var orientationInfo: OrientationInfo
var item: BaseItemDto = BaseItemDto()
@State private var episodes: [BaseItemDto] = []
@State private var isLoading: Bool = true
@State private var viewDidLoad: Bool = false
@ -27,10 +27,8 @@ struct SeasonItemView: View {
if(viewDidLoad) {
return
}
dump(item)
TvShowsAPI.getEpisodes(seriesId: item.seriesId!, userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio, .seasonUserData, .itemCounts, .overview], seasonId: item.id!)
TvShowsAPI.getEpisodes(seriesId: item.seriesId!, userId: globalData.user.user_id!, fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people], seasonId: item.id!)
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
isLoading = false

View File

@ -24,9 +24,9 @@ struct SeriesItemView: View {
if(viewDidLoad) {
return;
}
isLoading = true
TvShowsAPI.getSeasons(seriesId: item.id ?? "")
isLoading = true
TvShowsAPI.getSeasons(seriesId: item.id ?? "", fields: [.primaryImageAspectRatio,.seriesPrimaryImage,.seasonUserData,.overview,.genres,.people])
.sink(receiveCompletion: { completion in
HandleAPIRequestCompletion(globalData: globalData, completion: completion)
}, receiveValue: { response in

View File

@ -8,6 +8,7 @@
import SwiftUI
import MobileVLCKit
import JellyfinAPI
import MediaPlayer
struct Subtitle {
var name: String;
@ -185,9 +186,78 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
return true
}
func setupNowPlayingCC() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true;
commandCenter.pauseCommand.isEnabled = true;
commandCenter.seekForwardCommand.isEnabled = true;
commandCenter.seekBackwardCommand.isEnabled = true;
commandCenter.changePlaybackPositionCommand.isEnabled = true
// Add handler for Pause Command
commandCenter.pauseCommand.addTarget{ event in
self.mediaPlayer.pause()
self.sendProgressReport(eventName: "pause")
return .success
}
//Add handler for Play command
commandCenter.playCommand.addTarget{ event in
self.mediaPlayer.play()
self.sendProgressReport(eventName: "unpause")
return .success
}
//Add handler for FF command
commandCenter.seekForwardCommand.addTarget{ event in
self.mediaPlayer.jumpForward(30)
self.sendProgressReport(eventName: "timeupdate")
return .success
}
//Add handler for RW command
commandCenter.seekBackwardCommand.addTarget{ event in
self.mediaPlayer.jumpBackward(15)
self.sendProgressReport(eventName: "timeupdate")
return .success
}
//Scrubber
commandCenter.changePlaybackPositionCommand.addTarget { [weak self](remoteEvent) -> MPRemoteCommandHandlerStatus in
guard let self = self else {return .commandFailed}
if let event = remoteEvent as? MPChangePlaybackPositionCommandEvent {
let targetSeconds = event.positionTime
let videoPosition = Double(self.mediaPlayer.time.intValue)
let offset = targetSeconds - videoPosition;
if(offset > 0) {
self.mediaPlayer.jumpForward(Int32(offset)/1000);
} else {
self.mediaPlayer.jumpBackward(Int32(abs(offset))/1000);
}
self.sendProgressReport(eventName: "unpause")
return .success
} else {
return .commandFailed
}
}
var nowPlayingInfo = [String : Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = manifest.name ?? ""
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
UIApplication.shared.beginReceivingRemoteControlEvents()
}
override func remoteControlReceived(with event: UIEvent?) {
dump(event)
}
override func viewDidLoad() {
super.viewDidLoad()
//View has loaded.
//Rotate to landscape only if necessary
@ -200,7 +270,11 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
mediaPlayer.delegate = self
mediaPlayer.drawable = videoContentView
titleLabel.text = manifest.name
if(manifest.type == "Movie") {
titleLabel.text = manifest.name
} else {
titleLabel.text = "S\(String(manifest.parentIndexNumber!)):E\(String(manifest.indexNumber!))\(manifest.name!)"
}
//Fetch max bitrate from UserDefaults depending on current connection mode
let defaults = UserDefaults.standard
@ -292,9 +366,12 @@ class PlayerViewController: UIViewController, VLCMediaDelegate, VLCMediaPlayerDe
playbackItem = item;
}
self.setupNowPlayingCC()
DispatchQueue.global(qos: .background).async {
mediaPlayer.media = VLCMedia(url: playbackItem.videoUrl)
mediaPlayer.play()
print(manifest.userData?.playbackPositionTicks ?? 0)
mediaPlayer.jumpForward(Int32(manifest.userData?.playbackPositionTicks ?? 0/10000000))
mediaPlayer.pause()
subtitleTrackArray.forEach() { sub in

View File

@ -27,9 +27,9 @@ class justSignedIn: ObservableObject {
}
class GlobalData: ObservableObject {
@Published var user: SignedInUser = SignedInUser()
@Published var user: SignedInUser!
@Published var authToken: String = ""
@Published var server: Server = Server()
@Published var server: Server!
@Published var authHeader: String = ""
@Published var isInNetwork: Bool = true;
@Published var networkError: Bool = false;