Merge branch 'main' into main

This commit is contained in:
aiden 3 2021-08-01 12:26:49 -04:00 committed by GitHub
commit 9e0521b954
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 177 additions and 42 deletions

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -71,6 +71,7 @@
535870AD2669D8DD00D05A09 /* Typings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535870AC2669D8DD00D05A09 /* Typings.swift */; };
535BAE9F2649E569005FA86D /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAE9E2649E569005FA86D /* ItemView.swift */; };
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535BAEA4264A151C005FA86D /* VideoPlayer.swift */; };
53628C6D26B5AA0D008A64A0 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 53628C6C26B5AA0D008A64A0 /* Defaults */; };
53649AAD269CFAEA00A2D8B7 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = 53649AAC269CFAEA00A2D8B7 /* Puppy */; };
53649AAF269CFAF600A2D8B7 /* Puppy in Frameworks */ = {isa = PBXBuildFile; productRef = 53649AAE269CFAF600A2D8B7 /* Puppy */; };
53649AB1269CFB1900A2D8B7 /* LogManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53649AB0269CFB1900A2D8B7 /* LogManager.swift */; };
@ -97,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 */; };
@ -306,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>"; };
@ -413,6 +418,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
53628C6D26B5AA0D008A64A0 /* Defaults in Frameworks */,
628B95332670CAEA0091AF3B /* NukeUI in Frameworks */,
628B95242670CABD0091AF3B /* SwiftUI.framework in Frameworks */,
531ABF6C2671F5CC00C0FE20 /* WidgetKit.framework in Frameworks */,
@ -533,6 +539,7 @@
53116A16268B919A003024C9 /* SeriesItemView.swift */,
53272536268C1DBB0035FBF1 /* SeasonItemView.swift */,
53272538268C20100035FBF1 /* EpisodeItemView.swift */,
5398514426B64DA100101B49 /* SettingsView.swift */,
);
path = "JellyfinPlayer tvOS";
sourceTree = "<group>";
@ -632,7 +639,6 @@
53313B8F265EEA6D00947AA3 /* VideoPlayer.storyboard */,
532E68CE267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift */,
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */,
53DE4BD1267098F300739748 /* SearchBarView.swift */,
625CB5672678B6FB00530A6E /* SplashView.swift */,
625CB56B2678C0FD00530A6E /* MainTabView.swift */,
625CB56E2678C23300530A6E /* HomeView.swift */,
@ -690,6 +696,7 @@
621338912660106C00A81A2A /* Extensions */ = {
isa = PBXGroup;
children = (
53DE4BD1267098F300739748 /* SearchBarView.swift */,
53192D5C265AA78A008A4215 /* DeviceProfileBuilder.swift */,
531AC8BE26750DE20091C7EB /* ImageView.swift */,
5364F454266CA0DC0026ECBA /* APIExtensions.swift */,
@ -837,6 +844,7 @@
628B95392670CE250091AF3B /* KeychainSwift */,
536D3D7C267BD5F90004248C /* ActivityIndicator */,
53649AB4269D423A00A2D8B7 /* Puppy */,
53628C6C26B5AA0D008A64A0 /* Defaults */,
);
productName = WidgetExtensionExtension;
productReference = 628B95202670CABD0091AF3B /* WidgetExtension.appex */;
@ -1069,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 */,
@ -1083,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 */,
@ -1245,7 +1256,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = "JellyfinPlayer tvOS/JellyfinPlayer tvOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 58;
CURRENT_PROJECT_VERSION = 59;
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
DEVELOPMENT_TEAM = 9R8RREG67J;
ENABLE_PREVIEWS = YES;
@ -1276,7 +1287,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = "JellyfinPlayer tvOS/JellyfinPlayer tvOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 58;
CURRENT_PROJECT_VERSION = 59;
DEVELOPMENT_ASSET_PATHS = "\"JellyfinPlayer tvOS/Preview Content\"";
DEVELOPMENT_TEAM = 9R8RREG67J;
ENABLE_PREVIEWS = YES;
@ -1428,7 +1439,7 @@
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 58;
CURRENT_PROJECT_VERSION = 59;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 9R8RREG67J;
ENABLE_BITCODE = NO;
@ -1464,7 +1475,7 @@
CODE_SIGN_ENTITLEMENTS = JellyfinPlayer/JellyfinPlayer.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 58;
CURRENT_PROJECT_VERSION = 59;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = 9R8RREG67J;
@ -1497,7 +1508,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 58;
CURRENT_PROJECT_VERSION = 59;
DEVELOPMENT_TEAM = 9R8RREG67J;
INFOPLIST_FILE = WidgetExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1524,7 +1535,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 58;
CURRENT_PROJECT_VERSION = 59;
DEVELOPMENT_TEAM = 9R8RREG67J;
INFOPLIST_FILE = WidgetExtension/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.1;
@ -1706,6 +1717,11 @@
package = 621C637E26672A30004216EA /* XCRemoteSwiftPackageReference "NukeUI" */;
productName = NukeUI;
};
53628C6C26B5AA0D008A64A0 /* Defaults */ = {
isa = XCSwiftPackageProductDependency;
package = 62CB3F442685BAF7003D0A6F /* XCRemoteSwiftPackageReference "Defaults" */;
productName = Defaults;
};
53649AAC269CFAEA00A2D8B7 /* Puppy */ = {
isa = XCSwiftPackageProductDependency;
package = 53649AAB269CFAEA00A2D8B7 /* XCRemoteSwiftPackageReference "Puppy" */;

View File

@ -151,7 +151,7 @@ struct MovieItemView: View {
HStack {
Spacer().frame(width: 16)
ForEach(viewModel.item.people!, id: \.self) { person in
if person.type! == "Actor" {
if person.type ?? "" == "Actor" {
NavigationLink(destination: LazyView {
LibraryView(viewModel: .init(person: person), title: person.name ?? "")
}) {
@ -163,7 +163,7 @@ struct MovieItemView: View {
.cornerRadius(10)
Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1)
.frame(width: 100).foregroundColor(Color.primary)
if person.role != "" {
if person.role != nil {
Text(person.role!).font(.caption).fontWeight(.medium).lineLimit(1)
.foregroundColor(Color.secondary).frame(width: 100)
}

View File

@ -482,7 +482,6 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
let builder = DeviceProfileBuilder()
builder.setMaxBitrate(bitrate: maxBitrate)
let profile = builder.buildProfile()
dump(profile)
let playbackInfo = PlaybackInfoDto(userId: SessionManager.current.user.user_id!, maxStreamingBitrate: Int(maxBitrate), startTimeTicks: manifest.userData?.playbackPositionTicks ?? 0, deviceProfile: profile, autoOpenLiveStream: true)
DispatchQueue.global(qos: .userInitiated).async { [self] in
@ -505,9 +504,9 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
break
}
}, receiveValue: { [self] response in
dump(response)
playSessionId = response.playSessionId ?? ""
let mediaSource = response.mediaSources!.first.self!
dump(mediaSource)
if mediaSource.transcodingUrl != nil {
// Item is being transcoded by request of server
let streamURL = URL(string: "\(ServerEnvironment.current.server.baseURI!)\(mediaSource.transcodingUrl!)")
@ -654,7 +653,9 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
if fetchCaptions {
mediaPlayer.pause()
subtitleTrackArray.forEach { sub in
if sub.id != -1 && sub.delivery == .external {
//stupid fxcking jeff decides to re-encode these when added.
//only add playback streams when codec not supported by VLC.
if sub.id != -1 && sub.delivery == .external && sub.codec != "subrip" {
mediaPlayer.addPlaybackSlave(sub.url!, type: .subtitle, enforce: false)
}
}
@ -1013,7 +1014,7 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
func showLoadingView(_ viewController: PlayerViewController) {
self.loadBinding.wrappedValue = true
}
func exitPlayer(_ viewController: PlayerViewController) {
self.pBinding.wrappedValue = false
}
@ -1040,7 +1041,12 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
extension PlayerViewController {
func sendProgressReport(eventName: String) {
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: (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")
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: (mediaPlayer.state == .paused), 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

View File

@ -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=60&tag=\(imageTag)"
let urlString = "\(baseURL)/Items/\(self.id ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=85&tag=\(imageTag)"
return URL(string: urlString)!
}

View File

@ -112,6 +112,8 @@ class DeviceProfileBuilder {
subtitleProfiles.append(SubtitleProfile(format: "subrip", method: .embed))
subtitleProfiles.append(SubtitleProfile(format: "sub", method: .embed))
subtitleProfiles.append(SubtitleProfile(format: "pgssub", method: .embed))
//These need to be filtered. Most subrips are embedded. I hate subtitles.
subtitleProfiles.append(SubtitleProfile(format: "subrip", method: .external))
subtitleProfiles.append(SubtitleProfile(format: "sub", method: .external))
subtitleProfiles.append(SubtitleProfile(format: "ass", method: .external))

View File

@ -26,7 +26,7 @@ struct ImageView: View {
var body: some View {
LazyImage(source: source)
.placeholder {
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 8, height: 8))!)
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 8, height: 8)) ?? UIImage(blurHash: "001fC^", size: CGSize(width:8,height:8))!)
.resizable()
}
.failure {

View File

@ -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: {

View File

@ -23,14 +23,15 @@ final class SessionManager {
fileprivate(set) var user: SignedInUser!
fileprivate(set) var deviceID: String = ""
fileprivate(set) var accessToken: String = ""
#if os(tvOS)
let tvUserManager = TVUserManager()
#endif
let userDefaults = UserDefaults()
init() {
let savedUserRequest: NSFetchRequest<SignedInUser> = SignedInUser.fetchRequest()
let lastUsedUserID = userDefaults.string(forKey: "lastUsedUserID")
let savedUsers = try? PersistenceController.shared.container.viewContext.fetch(savedUserRequest)
#if os(tvOS)
@ -40,7 +41,15 @@ final class SessionManager {
}
}
#else
user = savedUsers?.first
if(lastUsedUserID != nil) {
savedUsers?.forEach { savedUser in
if(savedUser.user_id ?? "" == lastUsedUserID!) {
user = savedUser;
}
}
} else {
user = savedUsers?.first
}
#endif
if user != nil {
@ -116,7 +125,7 @@ final class SessionManager {
func loginWithSavedSession(user: SignedInUser) {
let accessToken = getAuthToken(userID: user.user_id!)
userDefaults.set(user.user_id!, forKey: "lastUsedUserID")
self.user = user
generateAuthHeader(with: accessToken, deviceID: user.device_uuid)
print(JellyfinAPI.customHeaders)
@ -135,7 +144,12 @@ final class SessionManager {
user.device_uuid = self.deviceID
#if os(tvOS)
// user.appletv_id = tvUserManager.currentUserIdentifier ?? ""
let descriptor: TVAppProfileDescriptor = TVAppProfileDescriptor(name: user.username!)
self.tvUserManager.shouldStorePreferenceForCurrentUser(to: descriptor) { should in
if(should) {
user.appletv_id = self.tvUserManager.currentUserIdentifier ?? ""
}
}
#endif
return (user, response.accessToken)
@ -164,9 +178,10 @@ final class SessionManager {
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
keychain.delete("AccessToken_\(user?.user_id ?? "")")
generateAuthHeader(with: nil, deviceID: nil)
let deleteRequest = NSBatchDeleteRequest(objectIDs: [user.objectID])
user = nil
_ = try? PersistenceController.shared.container.viewContext.execute(deleteRequest)
if(user != nil) {
let deleteRequest = NSBatchDeleteRequest(objectIDs: [user.objectID])
user = nil
_ = try? PersistenceController.shared.container.viewContext.execute(deleteRequest)
}
}
}

View File

@ -54,8 +54,8 @@ final class LibraryFilterViewModel: ViewModel {
init(filters: LibraryFilters? = nil,
enabledFilterType: [FilterType] = [.tag, .genre, .sortBy, .sortOrder, .filter], parentId: String) {
self.enabledFilterType = enabledFilterType
self.selectedSortBy = filters!.sortBy.first!
self.selectedSortOrder = filters!.sortOrder.first!
self.selectedSortBy = filters?.sortBy.first ?? .name;
self.selectedSortOrder = filters?.sortOrder.first ?? .descending;
self.parentId = parentId
super.init()

View File

@ -24,7 +24,7 @@ final class LibraryListViewModel: ViewModel {
}
func requestLibraries() {
UserViewsAPI.getUserViews(userId: SessionManager.current.user.user_id!)
UserViewsAPI.getUserViews(userId: SessionManager.current.user.user_id ?? "val was nil")
.trackActivity(loading)
.sink(receiveCompletion: { completion in
self.handleAPIRequestCompletion(completion: completion)