add tvOS settings page; fix transcoded video playback always starting at 0 ticks, also add front row image.
This commit is contained in:
parent
5b9e753965
commit
93a25eb9c4
|
@ -21,19 +21,6 @@ struct HomeView: View {
|
|||
ProgressView()
|
||||
} else {
|
||||
LazyVStack(alignment: .leading) {
|
||||
Button {
|
||||
let nc = NotificationCenter.default
|
||||
nc.post(name: Notification.Name("didSignOut"), object: nil)
|
||||
} label: {
|
||||
HStack {
|
||||
ImageView(src: URL(string: "\(ServerEnvironment.current.server.baseURI ?? "")/Users/\(SessionManager.current.user.user_id!)/Images/Primary?width=500")!)
|
||||
.frame(width: 50, height: 50)
|
||||
.cornerRadius(25.0)
|
||||
Text(SessionManager.current.user.username ?? "")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
}.padding(.leading, 90)
|
||||
if !viewModel.resumeItems.isEmpty {
|
||||
ContinueWatchingView(items: viewModel.resumeItems)
|
||||
}
|
||||
|
|
|
@ -53,6 +53,14 @@ struct MainTabView: View {
|
|||
Image(systemName: "folder")
|
||||
}
|
||||
.tag(Tab.allMedia)
|
||||
|
||||
SettingsView(viewModel: SettingsViewModel())
|
||||
.offset(y: -1) // don't remove this. it breaks tabview on 4K displays.
|
||||
.tabItem {
|
||||
Text("Settings")
|
||||
Image(systemName: "gear")
|
||||
}
|
||||
.tag(Tab.settings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +70,7 @@ extension MainTabView {
|
|||
enum Tab: String {
|
||||
case home
|
||||
case allMedia
|
||||
case settings
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* 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 CoreData
|
||||
import SwiftUI
|
||||
import Defaults
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
@ObservedObject var viewModel: SettingsViewModel
|
||||
|
||||
@Default(.inNetworkBandwidth) var inNetworkStreamBitrate
|
||||
@Default(.outOfNetworkBandwidth) var outOfNetworkStreamBitrate
|
||||
@Default(.isAutoSelectSubtitles) var isAutoSelectSubtitles
|
||||
@Default(.autoSelectSubtitlesLangCode) var autoSelectSubtitlesLangcode
|
||||
@Default(.autoSelectAudioLangCode) var autoSelectAudioLangcode
|
||||
@State private var username: String = ""
|
||||
|
||||
func onAppear() {
|
||||
username = SessionManager.current.user?.username ?? ""
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header: Text("Playback settings")) {
|
||||
Picker("Default local quality", selection: $inNetworkStreamBitrate) {
|
||||
ForEach(self.viewModel.bitrates, id: \.self) { bitrate in
|
||||
Text(bitrate.name).tag(bitrate.value)
|
||||
}.padding(.leading, 90)
|
||||
.padding(.trailing, 90)
|
||||
}
|
||||
|
||||
Picker("Default remote quality", selection: $outOfNetworkStreamBitrate) {
|
||||
ForEach(self.viewModel.bitrates, id: \.self) { bitrate in
|
||||
Text(bitrate.name).tag(bitrate.value)
|
||||
}.padding(.leading, 90)
|
||||
.padding(.trailing, 90)
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Accessibility")) {
|
||||
Toggle("Automatically show subtitles", isOn: $isAutoSelectSubtitles)
|
||||
SearchablePicker(label: "Preferred subtitle language",
|
||||
options: viewModel.langs,
|
||||
optionToString: { $0.name },
|
||||
selected: Binding<TrackLanguage>(
|
||||
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectSubtitlesLangcode }) ?? .auto },
|
||||
set: {autoSelectSubtitlesLangcode = $0.isoCode}
|
||||
)
|
||||
)
|
||||
SearchablePicker(label: "Preferred audio language",
|
||||
options: viewModel.langs,
|
||||
optionToString: { $0.name },
|
||||
selected: Binding<TrackLanguage>(
|
||||
get: { viewModel.langs.first(where: { $0.isoCode == autoSelectAudioLangcode }) ?? .auto },
|
||||
set: { autoSelectAudioLangcode = $0.isoCode}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Section(header: Text(ServerEnvironment.current.server.name ?? "")) {
|
||||
HStack {
|
||||
Text("Signed in as \(username)").foregroundColor(.primary)
|
||||
Spacer()
|
||||
Button {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
let nc = NotificationCenter.default
|
||||
nc.post(name: Notification.Name("didSignOut"), object: nil)
|
||||
}
|
||||
} label: {
|
||||
Text("Switch user").font(.callout)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
SessionManager.current.logout();
|
||||
let nc = NotificationCenter.default
|
||||
nc.post(name: Notification.Name("didSignOut"), object: nil)
|
||||
}
|
||||
} label: {
|
||||
Text("Sign out").font(.callout)
|
||||
}
|
||||
}
|
||||
}.onAppear(perform: onAppear)
|
||||
.padding(.leading, 90)
|
||||
.padding(.trailing, 90)
|
||||
}
|
||||
}
|
|
@ -582,7 +582,12 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate,
|
|||
updateNowPlayingCenter(time: nil, playing: mediaPlayer.state == .playing)
|
||||
|
||||
if (eventName == "timeupdate" && mediaPlayer.state == .playing) || eventName != "timeupdate" {
|
||||
let progressInfo = PlaybackProgressInfo(canSeek: true, item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, audioStreamIndex: Int(selectedAudioTrack), subtitleStreamIndex: Int(selectedCaptionTrack), isPaused: (!playing), 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")
|
||||
var ticks: Int64 = Int64(mediaPlayer.position * Float(manifest.runTimeTicks!));
|
||||
if(ticks == 0) {
|
||||
ticks = manifest.userData?.playbackPositionTicks ?? 0
|
||||
}
|
||||
|
||||
let progressInfo = PlaybackProgressInfo(canSeek: true, item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, audioStreamIndex: Int(selectedAudioTrack), subtitleStreamIndex: Int(selectedCaptionTrack), isPaused: (!playing), isMuted: false, positionTicks: ticks, 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: { result in
|
||||
|
|
|
@ -98,6 +98,9 @@
|
|||
53892770263C25230035E14B /* NextUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5389276F263C25230035E14B /* NextUpView.swift */; };
|
||||
53892772263C8C6F0035E14B /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53892771263C8C6F0035E14B /* LoadingView.swift */; };
|
||||
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5389277B263CC3DB0035E14B /* BlurHashDecode.swift */; };
|
||||
5398514526B64DA100101B49 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5398514426B64DA100101B49 /* SettingsView.swift */; };
|
||||
5398514626B64DBB00101B49 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
|
||||
5398514726B64E4100101B49 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DE4BD1267098F300739748 /* SearchBarView.swift */; };
|
||||
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */; };
|
||||
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A089CF264DA9DA00D57806 /* MovieItemView.swift */; };
|
||||
53A431BD266B0FF20016769F /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 53A431BC266B0FF20016769F /* JellyfinAPI */; };
|
||||
|
@ -307,6 +310,7 @@
|
|||
5389276F263C25230035E14B /* NextUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextUpView.swift; sourceTree = "<group>"; };
|
||||
53892771263C8C6F0035E14B /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
|
||||
5389277B263CC3DB0035E14B /* BlurHashDecode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
||||
5398514426B64DA100101B49 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
53987CA326572C1300E7EA70 /* SeasonItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeasonItemView.swift; sourceTree = "<group>"; };
|
||||
53987CA526572F0700E7EA70 /* SeriesItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeriesItemView.swift; sourceTree = "<group>"; };
|
||||
53987CA72657424A00E7EA70 /* EpisodeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeItemView.swift; sourceTree = "<group>"; };
|
||||
|
@ -535,6 +539,7 @@
|
|||
53116A16268B919A003024C9 /* SeriesItemView.swift */,
|
||||
53272536268C1DBB0035FBF1 /* SeasonItemView.swift */,
|
||||
53272538268C20100035FBF1 /* EpisodeItemView.swift */,
|
||||
5398514426B64DA100101B49 /* SettingsView.swift */,
|
||||
);
|
||||
path = "JellyfinPlayer tvOS";
|
||||
sourceTree = "<group>";
|
||||
|
@ -634,7 +639,6 @@
|
|||
53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */,
|
||||
532E68CE267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift */,
|
||||
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */,
|
||||
53DE4BD1267098F300739748 /* SearchBarView.swift */,
|
||||
625CB5672678B6FB00530A6E /* SplashView.swift */,
|
||||
625CB56B2678C0FD00530A6E /* MainTabView.swift */,
|
||||
625CB56E2678C23300530A6E /* HomeView.swift */,
|
||||
|
@ -692,6 +696,7 @@
|
|||
621338912660106C00A81A2A /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
53DE4BD1267098F300739748 /* SearchBarView.swift */,
|
||||
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */,
|
||||
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
||||
5364F454266CA0DC0026ECBA /* APIExtensions.swift */,
|
||||
|
@ -1072,6 +1077,7 @@
|
|||
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
||||
53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */,
|
||||
62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
||||
5398514526B64DA100101B49 /* SettingsView.swift in Sources */,
|
||||
62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
||||
5310695A2684E7EE00CFFDBA /* VideoPlayer.swift in Sources */,
|
||||
53ABFDE6267974EF00886593 /* SettingsViewModel.swift in Sources */,
|
||||
|
@ -1086,8 +1092,10 @@
|
|||
536D3D81267BDFC60004248C /* PortraitItemElement.swift in Sources */,
|
||||
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */,
|
||||
5310695B2684E7EE00CFFDBA /* AudioView.swift in Sources */,
|
||||
5398514726B64E4100101B49 /* SearchBarView.swift in Sources */,
|
||||
091B5A8D268315D400D78B61 /* ServerDiscovery.swift in Sources */,
|
||||
53ABFDE7267974EF00886593 /* ConnectToServerViewModel.swift in Sources */,
|
||||
5398514626B64DBB00101B49 /* SearchablePickerView.swift in Sources */,
|
||||
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */,
|
||||
62CB3F4C2685BB77003D0A6F /* DefaultsExtension.swift in Sources */,
|
||||
62E632E4267D3BA60063E547 /* MovieItemViewModel.swift in Sources */,
|
||||
|
|
|
@ -70,7 +70,7 @@ extension BaseItemDto {
|
|||
}
|
||||
|
||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)&format=webp"
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||
return URL(string: urlString)!
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ extension BaseItemDto {
|
|||
let imageTag = (self.parentBackdropImageTags ?? [""])[0]
|
||||
|
||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.parentBackdropItemId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)&format=webp"
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.parentBackdropItemId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||
return URL(string: urlString)!
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ extension BaseItemDto {
|
|||
let imageType = "Primary"
|
||||
let imageTag = self.seriesPrimaryImageTag ?? ""
|
||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)&format=webp"
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||
return URL(string: urlString)!
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ extension BaseItemDto {
|
|||
|
||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)&format=webp"
|
||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||
// print(urlString)
|
||||
return URL(string: urlString)!
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ extension BaseItemPerson {
|
|||
|
||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||
|
||||
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)&format=webp"
|
||||
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
|
||||
return URL(string: urlString)!
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ struct SearchBar: View {
|
|||
TextField(NSLocalizedString("Search...", comment: ""), text: $text)
|
||||
.padding(8)
|
||||
.padding(.horizontal, 16)
|
||||
#if os(iOS)
|
||||
.background(Color(.systemGray6))
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
if !text.isEmpty {
|
||||
Button(action: {
|
Loading…
Reference in New Issue